I’ve recently read a lot of arguments against using the repository pattern with powerful ORM’s like Entity Framework as it incorporates repository-like functionality, along with Unit of Work functionality as well.
Another argument against using the pattern for a situation like unit testing is that the repository pattern is a leaky abstraction since the more generic implementations leverage IQueryable.
The arguments against using the repository pattern make sense to me, but the alternative methods of abstractions suggested are often more confusing and appear just as overkill as the problem.
Jimmy Bogards solution seems to be a mix of blowing the abstractions away, but also introducing his own architecture.
https://lostechies.com/jimmybogard/2012/10/08/favor-query-objects-over-repositories/
Another example of repositories being unnecessarily….but use my architecture!
http://blog.gauffin.org/2012/10/22/griffin-decoupled-the-queries/
Another…
http://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework
I haven’t found a clear replacement or alternative to the “overly complex” repository pattern approach that isn’t more architected itself.
7
I think you are conflating repositories and generic repositories.
A basic repository just interfaces your data store and provides methods to return the data
IRepository {
List<Data> GetDataById(string id);
}
It doesn’t leak the data layer into your code via an IQueryable or other ways of passing in random queries and provides a well defined testable and injectable surface of methods.
A Generic Repository allows you to pass in your query much like an ORM
IGenericRepository<T> {
List<T> Get<T>(IQuery query);
//or
IQueryable<T> Get<T>();
}
I agree there isn’t much point using a Generic Repository on top of an ORM which is basically just another Generic Repository.
The answer is to use the basic Repository pattern to hide your ORM
2
Most of the arguments you mention wrongly attribute to the Repository pattern features that it doesn’t have.
Conceptually, a Repository as originally defined in DDD is just a collection of objects that you can search or add to. The persistence mechanism behind it is abstracted out, so as a consumer you get the illusion that it’s an in-memory collection.
A Repository implementation that has leaky abstractions (exposing IQueryables
for instance) is a poor Repository implementation.
A Repository implementation that exposes more than just collection operations (for instance, Unit of Work features) is a poor Repository implementation.
Are there alternatives to Repository for data access ? Yes, but they are not related to the issues you bring up in your question.
1
To me, repositories, combined with ORM or other DB persistence layers, have these disadvantages:
- Covering up Units of Work. UoW have to be coded by the programmer and can rarely be implemented as a kind of magic in the background, where the user simply makes queries and modifications, without defining the UoW boundaries and possibly the commit point. Sometimes, the UoW are abandoned by reducing them into micro UoW (e.g. NHibernate sessions) in each Repository access method.
- Covering up, or, in the worst case, destroying Persistence Ignorance: Methods like “Load()”, “Get()”, “Save()” or “Update()” suggest immediate, single object operations, as if sending individual SQL/DML, or as if working with files. In fact, for example, the NHibernate methods, with these misleading names, usually don’t make individual access, but enqueue for lazy load or insert/update batch (Persistence Ignorance). Sometimes, programmers wonder why they don’t get immediate DB operations and forcibly break up persistence ignorance, thus killing performance and using major efforts to actually make the system (much!) worse.
- Uncontrolled Growth. A simple repository might accumulate more and more methods to fit specific needs.
Such as:
public interface ICarsRepository /* initial */
{
ICar CreateNewCar();
ICar LoadCar(int id); // bad, should be for multiple IDs.
void SaveCar(ICar carToSave); // bad, no individual saves, use UoW commit!
}
public interface ICarsRepository /* a few years later */
{
ICar CreateNewCar();
ICar LoadCar(int id);
IList<ICar> GetBlueCars();
IList<ICar> GetRedYellowGreenCars();
IList<ICar> GetCarsByColor(Color colorOfCars); // a bit better
IList<ICar> GetCarsByColor(IEnumerable<Color> colorsOfCars); // better!
IList<ICar> GetCarsWithPowerBetween(int hpFrom, int hpTo);
IList<ICar> GetCarsWithPowerKwBetween(int kwFrom, int kwTo);
IList<ICar> GetCarsBuiltBetween(int yearFrom, int yearTo);
IList<ICar> GetCarsBuiltBetween(DateTime from, DateTime to); // some also need month and day
IList<ICar> GetHybridCarsBuiltBetween(DateTime from, DateTime to);
IList<ICar> GetElectricCarsBuiltBetween(DateTime from, DateTime to);
IList<ICar> GetCarsFromManufacturer(IManufacturer carManufacturer);
bool HasCarMeanwhileBeenChangedBySomebodyElseInDb(ICar car); // persistence ignorance broken
void SaveCar(ICar carToSave);
}
4. Danger of God object: you might be tempted to create one god class, covering all of your model or data access layer. The repository class would not only contain Car methods, but methods for all entities.
In my opinion, it is better to offer at least some query opportunities, to avoid the huge mess of many single purpose methods. No matter if it is LINQ, an own query language, or even something taken directly from the ORM (OK, kind of coupling problem…).
2
If the purpose of the Repository-interface is to mock away the database for a unittest (= test in isolation) the best abstraction is something that is easy to mock.
It is difficuilt to mock a repository interface that is based on a IQueryable result.
From a Unit-testing point of view
IRepository {
List<Data> GetDataById(string id);
}
can be mocked easily
IGenericRepository<T> {
List<T> Get<T>(IQuery query);
}
can be mocked easy only if the mock ignores the content of the query parameter.
IGenericRepository<T> {
IQueryable<T> Get<T>(some_parameters);
}
cannot be easily mocked
I don’t think repository pattern is overkill, if you use lambda functions for querying. Especially when you have to abstract the ORM (in my opinion you always should) then I won’t care the implementation details of the repository itself.
For example:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
UserRepository ur = new UserRepository();
var userWithA = ur.GetBy(u => u.Name.StartsWith("A"));
Console.WriteLine(userWithA.Name);
ur.GetAllBy(u => u.Name.StartsWith("M"))
.ForEach(u => Console.WriteLine(u.Name));
ur.GetAllBy(u => u.Age > 13)
.ForEach(u => Console.WriteLine(u.Name));
}
}
public class UserRepository
{
List<User> users = new List<User> {
new User{Name="Joe", Age=10},
new User{Name="Allen", Age=12},
new User{Name="Martin", Age=14},
new User{Name="Mary", Age=15},
new User{Name="Ashton", Age=29}
};
public User GetBy(Predicate<User> userPredicate)
{
return users.Find(userPredicate);
}
public List<User> GetAllBy(Predicate<User> userPredicate)
{
return users.FindAll(userPredicate);
}
}
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}