I have had lengthy conversations on the topic of using a generic repository pattern on top of Entity Framework (I’m kainazzzo). I believe that I am probably in a minority of developers that think it’s a perfectly acceptable practice. Here are some of the arguments people make about using IRepository<T>:

  1. It couples your class directly to Entity Framework, making it difficult to switch
  2. There is no value-add using a generic repository over top of EF, because it is already a Repository pattern
    • i.e. DbSet<Entity> is the Repository, and DbContext is the UnitOfWork
  3. It causes “leaky” abstraction, encouraging .Include() calls to be made in your business layer code
    • Or that in general, IQueryable should not be returned from a repository, because deferred queries are leaky

I will address these points directly.

It couples your class directly to Entity Framework, making it difficult to switch

The last time I changed data access in running production code was never. There would have to be an astronomically large gulf in the functionality between two ORM frameworks for this to even be a consideration. Once code is in production and being used by real live people, any change is risk. Risk is not taken unless the change is deemed to be of some value. Simply changing ORM or data access frameworks would be a tough sell to any business unit. Once you choose Entity Framework, you will not change to NHibernate. It is just not going to happen, so this is an difficult argument to make in my opinion. If you really have a problem with it, you can still do a higher level implementation on top of EFRepository that abstracts away the rest of the EF bits.

There is no value-add using a generic repository over top of EF, because it is already a Repository pattern

BOLOGNA SANDWICH. Seriously… now I’m hungry for processed meat.

What I mean is that this is preposterous. Take my IRepository<T> class for example:

public interface IRepository<T>
    {
        void InsertOrUpdate(T entity);
        void Remove(T entity);
        IQueryable<T> Find(Expression<Func<T, bool>> predicate);
        IQueryable<T> FindAll();
        T First(Expression<Func<T, bool>> predicate);
        T FirstOrDefault(Expression<Func<T, bool>> predicate);
    }

And the EF implementation:

    public class EFRepository<T> : IRepository<T>
        where T : class, IEntity
    {
        private readonly IUnitOfWork<T> _unitOfWork;
        private readonly DbSet<T> _dbSet;

        public EFRepository(IUnitOfWork<T> unitOfWork)
        {
            _unitOfWork = unitOfWork;
            _dbSet = _unitOfWork.DbSet;
        }

        public void InsertOrUpdate(T entity)
        {
            if (entity.Id != default(int))
            {
                if (_unitOfWork.Entry(entity).State == EntityState.Detached)
                {
                    _dbSet.Add(entity);
                }
                _unitOfWork.Entry(entity).State = EntityState.Modified;
            }
            else
            {
                _dbSet.Add(entity);
                _unitOfWork.DbSet.Add(entity);
            }
        }

        public void Remove(T entity)
        {
            _dbSet.Remove(entity);
        }

        public IQueryable<T> Find(Expression<Func<T, bool>> predicate)
        {
            return FindAll().Where(predicate);
        }

        public IQueryable<T> FindAll()
        {
            return _dbSet;
        }

        public T First(Expression<Func<T, bool>> predicate)
        {
            return FindAll().First(predicate);
        }

        public T FirstOrDefault(Expression<Func<T, bool>> predicate)
        {
            return FindAll().FirstOrDefault(predicate);
        }
    }

How many times have you looked up how to do “Insert or Update” in EF? I don’t have to do that any more. I know my repository does it well, and all I have to do is make my entities implement IEntity, which simply ensures that an Id field exists. This is not a problem with DB first implementations, since EF generates classes as partials. My value-add comes from EFRepository implementing basic Id checking in order to initiate a proper update in EF.

Then, there is unit testing. Some people would argue the value-add of unit tests, but I see unit tests as a priceless artifact one puts on display and protects. They are a window into your code and how it is supposed to function, and they can be a huge safety net. See my post on TDD for more information. Let’s say you were developing a game data editor and you had a class that relied on loading Enemies (EnemyEditor), perhaps one of the simple requirements was that when loading enemies (GetEnemies()), the list of Abilities was not null, even if the database provided it as such:

[TestMethod]
public void enemies_have_non_null_ability_list()
{
    var container = new MockingContainer<EnemyEditor>();
    container.Arrange<IRepository<Enemy>>(r => r.FindAll()).Returns(
        new List<Enemy>
        {
            new Enemy
            {
                Name = "Enemy1",
                Abilities = null
            }
        });

    var enemies = container.Instance.GetEnemies();

    Assert.IsNotNull(enemies[0].Abilities);
}

This unit test is for a very small requirement, and the arrangement of what IRepository<Enemy> returned was extremely easy to write (if you’re familiar with mocking). You didn’t have to jump through any hoops to mock up the DbSet within the DbContext by creating IDbContext or anything, which admittedly is not impossible, but it isn’t entirely intuitive, all apt alliteration aside.

So there is value in layering on top of Entity Framework with a generic repository pattern. The value is in InsertOrUpdate, as well as simplified unit test mocking.

It causes “leaky” abstraction, encouraging .Include() calls to be made in your business layer code

Given the value that it adds, I am totally alright with this. Yes, IQueryable.Include “leaks” implementation details through the abstraction layer, because .Include() only exists in Entity Framework; however, given that I already stated the fact that the ORM will never change, I am alright with making a conscious decision NOT to abstract away the ORM. I am not abstracting away Entity Framework… it is already an abstraction of data access that I am comfortable wtih. I am actually EXTENDING Entity Framework by adding a generic repository pattern on top of it. It is much simpler to deal with IRepository<T> and IUnitOfWork<T> than to have some GameEntities override that mocks the DbSet objects inside. Also, I will say it again: you can still use the generic repository pattern as a starting point, and further abstract away the EF specific bits by using a more domain-centric data access object.

It is my experienced opinion that software development is always about trade-offs. There is never a one-size-fits-all solution, but there are patterns that get us as far as 99% of the way there. It’s up to us as software craftspeople to massage and mold code into a product that works and is not a burden to maintain.

I leave you with two quotes from Bruce Lee:

“Don’t think, Feel, it is like a finger pointing out to the moon, don’t concentrate on the finger or you will miss all that heavenly glory.”

To me, coming up with reasons not to do the most obvious, simply because it is not exactly like you think it should be is like looking at the finger.

“All fixed set patterns are incapable of adaptability or pliability. The truth is outside of all fixed patterns.”

Without adapting the known repository pattern to fit the Entity Framework appropriately, we limit ourselves to something that is not ideal for the given circumstance.

 

Leave a Reply

Your email address will not be published. Required fields are marked *