C# Generic Repository – Real Advantages and Best Practices

asp.netcentity-framework

Was reading through some articles on the advantages of creating Generic Repositories for a new app (example). The idea seems nice because it lets me use the same repository to do several things for several different entity types at once:

IRepository repo = new EfRepository(); // Would normally pass through IOC into constructor 
var c1 = new Country() { Name = "United States", CountryCode = "US" };
var c2 = new Country() { Name = "Canada", CountryCode = "CA" };
var c3 = new Country() { Name = "Mexico", CountryCode = "MX" };
var p1 = new Province() { Country = c1, Name = "Alabama", Abbreviation = "AL" };
var p2 = new Province() { Country = c1, Name = "Alaska", Abbreviation = "AK" };
var p3 = new Province() { Country = c2, Name = "Alberta", Abbreviation = "AB" };
repo.Add<Country>(c1);
repo.Add<Country>(c2);
repo.Add<Country>(c3);
repo.Add<Province>(p1);
repo.Add<Province>(p2);
repo.Add<Province>(p3);
repo.Save();

However, the rest of the implementation of the Repository has a heavy reliance on Linq:

IQueryable<T> Query();
IList<T> Find(Expression<Func<T,bool>> predicate);
T Get(Expression<Func<T,bool>> predicate);
T First(Expression<Func<T,bool>> predicate);
//... and so on

This repository pattern worked fantastic for Entity Framework, and pretty much offered a 1 to 1 mapping of the methods available on DbContext/DbSet. But given the slow uptake of Linq on other data access technologies outside of Entity Framework, what advantage does this provide over working directly with the DbContext?

I attempted to write a PetaPoco version of the Repository, but PetaPoco doesn't support Linq Expressions, which makes creating a generic IRepository interface pretty much useless unless you only use it for the basic GetAll, GetById, Add, Update, Delete, and Save methods and utilize it as a base class. Then you have to create specific repositories with specialized methods to handle all the "where" clauses that I could previously pass in as a predicate.

Is the Generic Repository pattern useful for anything outside of Entity Framework? If not, why would someone use it at all instead of working directly with Entity Framework?


Original link doesn't reflect the pattern I was using in my sample code. Here is an (updated link).

Best Answer

Generic repository is even useless (and IMHO also bad) for Entity Framework. It doesn't bring any additional value to what is already provided by IDbSet<T> (which is btw. generic repository).

As you have already found the argument that generic repository can be replaced by implementation for other data access technology is pretty weak because it can demand writing your own Linq provider.

The second common argument about simplified unit testing is also wrong because mocking repository / set with in-memory data storage replaces Linq provider with another one which has different capabilities. Linq-to-entities provider supports only subset of Linq features - it even doesn't support all methods available on IQueryable<T> interface. Sharing expression trees between data access layer and business logic layers prevents any faking of data access layer - query logic must be separated.

If you want to have strong "generic" abstraction you must involve other patterns as well. In this case you need to use abstract query language which can be translated by repository to specific query language supported by used data access layer. This is handled by Specification pattern. Linq on IQueryable is specification (but the translation requires provider - or some custom visitor translating expression tree into query) but you can define your own simplified version and use it. For example NHibernate uses Criteria API. Still the simplest way is using specific repository with specific methods. This way is the simplest to implement, simplest to test and simplest to fake in unit tests because the query logic is completely hidden and separated behind the abstraction.