Entity Framework Generic Repository Pattern

  • Thought 1



    public interface IRepository<T> : IDisposable where T : class
    {
    IQueryable<T> Fetch();
    IEnumerable<T> GetAll();
    IEnumerable<T> Find(Func<T, bool> predicate);
    T Single(Func<T, bool> predicate);
    T First(Func<T, bool> predicate);
    void Add(T entity);
    void Delete(T entity);
    void SaveChanges();
    }


    with the above approach I will go like Repository and then have a Repository
    with the implementation of methods written above.



    I think this frees my tests from DbContext dependency.



    Thought 2



     public class RepositoryBase<TContext> : IDisposable where TContext : DbContext, new()
    {
    private TContext _DataContext;

    protected virtual TContext DataContext
    {
    get
    {
    if (_DataContext == null)
    {
    _DataContext = new TContext();
    }
    return _DataContext;
    }
    }

    public virtual IQueryable<T> GetAll<T>() where T : class
    {
    using (DataContext)
    {
    return DataContext.Set<T>();
    }
    }

    public virtual T FindSingleBy<T>(Expression<Func<T, bool>> predicate) where T : class
    {
    if (predicate != null)
    {
    using (DataContext)
    {
    return DataContext.Set<T>().Where(predicate).SingleOrDefault();
    }
    }
    else
    {
    throw new ArgumentNullException("Predicate value must be passed to FindSingleBy<T>.");
    }
    }

    public virtual IQueryable<T> FindAllBy<T>(Expression<Func<T, bool>> predicate) where T : class
    {
    if (predicate != null)
    {
    using (DataContext)
    {
    return DataContext.Set<T>().Where(predicate);
    }
    }
    else
    {
    throw new ArgumentNullException("Predicate value must be passed to FindAllBy<T>.");
    }
    }

    public virtual IQueryable<T> FindBy<T, TKey>(Expression<Func<T, bool>> predicate,Expression<Func<T, TKey>> orderBy) where T : class
    {
    if (predicate != null)
    {
    if (orderBy != null)
    {
    using (DataContext)
    {
    return FindAllBy<T>(predicate).OrderBy(orderBy).AsQueryable<T>(); ;
    }
    }
    else
    {
    throw new ArgumentNullException("OrderBy value must be passed to FindBy<T,TKey>.");
    }
    }
    else
    {
    throw new ArgumentNullException("Predicate value must be passed to FindBy<T,TKey>.");
    }
    }

    public virtual int Save<T>(T Entity) where T : class
    {
    return DataContext.SaveChanges();
    }
    public virtual int Update<T>(T Entity) where T : class
    {
    return DataContext.SaveChanges();
    }
    public virtual int Delete<T>(T entity) where T : class
    {
    DataContext.Set<T>().Remove(entity);
    return DataContext.SaveChanges();
    }
    public void Dispose()
    {
    if (DataContext != null) DataContext.Dispose();
    }
    }


    I could now have CustomerRepository : RepositoryBase<Mycontext>,ICustomerRepository
    ICustomerRepository gives me the option of defining stuff like GetCustomerWithOrders etc...



    I am confused as to which approach to take. Would need help through a review on the implementation first then comments on which one will be the easier approach when it comes to Testing and extensibility.


    Why not have both and have your RepositoryBase implement the IRepository interface?

    @dreza then, should the IRepository have methods like add , save , delete or should it have the find , get , fetch methods ? I guess both are addressing different concerns one set of methods is query and other is transactional logic, and the subsequent question is what are the benefits/drawbacks of making the RepositoryBase implement the IRepository interface ?

    I would make the repository have methods like SingleOrDefault(), GetAll(), FirstOrDefault() rather than having to do .Fetch().SingleOrDefault() etc

    About edit 2: Tests of the RepositoryBase has to be "integrated" since DbContext isn't abstract or have any good interface, but a nice trick is to use Database.SetInitializer(new DropCreateDatabaseAlways()) in those tests. Which means you always have a fresh DB for your integrated tests. It's also useful to move the concrete Repository and UnitOfWork classes and tests into separate assemblies, so you don't have those dependencies in your business logic tests and classes. Otherwise you could always go buy TypeMock which can mock more or less anything.

    @Lars-Erik So RepositoryBase will be tested through an Integration test with a database. I should be able to however test my business logic through unit tests. My POCO classes are in a separate assembly and the repository is in a separate assembly. My unit tests project just has a reference to the Repository. Is that the correct structure ?

    Keep two unit test projects. One that references the poco assembly (which doesn't have dependency on concrete uow/repo), and one that references both for integrated tests.

    What can I test in the project that references the POCO assembly ? I was expecting to text the logic in my repository in one project and have a db backed integration in another.

    The conrete repo is tightly copuled to your ORM (Entity Framework), so it is per definition DB backend integration. The repo interface however is not, so it can be mocked and used in business logic tests.

  • Lars-Erik

    Lars-Erik Correct answer

    8 years ago

    You should use Expression<Func<T, bool>> as predicates on your interface and have the RepositoryBase implement that interface.
    By only using Func, you won't get translation to L2E etc, but will have to enumerate the entire DB table before you can evaluate the Func.
    The interface can be mocked, hence unit-tested without a physical db and also used with other ORMs.



    It's generally prefered to keep SaveChanges and the DbContext in a separate IUnitOfWork implementation. You can pass the UnitOfWork to the repository constructor, which can access an internal property on the UOW exposing the DbContext.



    By doing that you can share the same DbContext between repositories, and batch updates to several entity roots of different type. You also keep fewer open connections, which are the most expensive resource you have when it comes to DBs.



    Not to forget they can share transactions. :)



    Hence you should not dispose the DbContext in the Repository, the Repository really doesn't need to be disposable at all. But the UnitOfWork / DbContext must be disposed by something.



    Also, ditch the OrderBy predicate to FindBy. Since you return an IQueryable, and use an Expression for the predicate, you can keep building the Queryable after the call. For instance repo.FindBy(it => it.Something == something).OrderBy(it => it.OrderProperty). It will still be translated to "select [fields] from [table] where [predicate] order by [orderprop]" when enumerated.



    Otherwise it looks good.



    Here's a couple of good examples:



    http://blog.swink.com.au/index.php/c-sharp/generic-repository-for-entity-framework-4-3-dbcontext-with-code-first/



    http://www.martinwilley.com/net/code/data/genericrepository.html



    http://www.mattdurrant.com/ef-code-first-with-the-repository-and-unit-of-work-patterns/



    And here's how I do it:



    Model / Common / Business assembly



    No references but BCL and other possible models (interfaces/dtos)



    public interface IUnitOfWork : IDisposable
    {
    void Commit();
    }

    public interface IRepository<T>
    {
    void Add(T item);
    void Remove(T item);
    IQueryable<T> Query();
    }

    // entity classes


    Business tests assembly



    Only references Model / Common / Business assembly



    [TestFixture]
    public class BusinessTests
    {
    private IRepository<Entity> repo;
    private ConcreteService service;

    [SetUp]
    public void SetUp()
    {
    repo = MockRepository.GenerateStub<IRepository<Entity>>();
    service = new ConcreteService(repo);
    }

    [Test]
    public void Service_DoSomething_DoesSomething()
    {
    var expectedName = "after";
    var entity = new Entity { Name = "before" };
    var list = new List<Entity> { entity };
    repo.Stub(r => r.Query()).Return(list.AsQueryable());
    service.DoStuff();
    Assert.AreEqual(expectedName, entity.Name);
    }

    }


    Entity Framework implementation assembly



    References Model and the Entity Framework / System.Data.Entity assemblies



    public class EFUnitOfWork : IUnitOfWork
    {
    private readonly DbContext context;

    public EFUnitOfWork(DbContext context)
    {
    this.context = context;
    }

    internal DbSet<T> GetDbSet<T>()
    where T : class
    {
    return context.Set<T>();
    }

    public void Commit()
    {
    context.SaveChanges();
    }

    public void Dispose()
    {
    context.Dispose();
    }
    }

    public class EFRepository<T> : IRepository<T>
    where T : class
    {
    private readonly DbSet<T> dbSet;

    public EFRepository(IUnitOfWork unitOfWork)
    {
    var efUnitOfWork = unitOfWork as EFUnitOfWork;
    if (efUnitOfWork == null) throw new Exception("Must be EFUnitOfWork"); // TODO: Typed exception
    dbSet = efUnitOfWork.GetDbSet<T>();
    }

    public void Add(T item)
    {
    dbSet.Add(item);
    }

    public void Remove(T item)
    {
    dbSet.Remove(item);
    }

    public IQueryable<T> Query()
    {
    return dbSet;
    }
    }


    Integrated tests assembly



    References everything



    [TestFixture]
    [Category("Integrated")]
    public class IntegratedTest
    {
    private EFUnitOfWork uow;
    private EFRepository<Entity> repo;

    [SetUp]
    public void SetUp()
    {
    Database.SetInitializer(new DropCreateDatabaseAlways<YourContext>());
    uow = new EFUnitOfWork(new YourContext());
    repo = new EFRepository<Entity>(uow);
    }

    [TearDown]
    public void TearDown()
    {
    uow.Dispose();
    }

    [Test]
    public void Repository_Add_AddsItem()
    {
    var expected = new Entity { Name = "Test" };
    repo.Add(expected);
    uow.Commit();
    var actual = repo.Query().FirstOrDefault(e => e.Name == "Test");
    Assert.IsNotNull(actual);
    }

    [Test]
    public void Repository_Remove_RemovesItem()
    {
    var expected = new Entity { Name = "Test" };
    repo.Add(expected);
    uow.Commit();
    repo.Remove(expected);
    uow.Commit();
    Assert.AreEqual(0, repo.Query().Count());
    }
    }

    Expression v/s Func ..nice catch.. the RepositoryBase has the correct predicate. OrderBy also looked useless to me. Will remove that. The link you provide have two different implementations in terms of passing context or passing context and entity type as Type parameters. Which one is the better one or are both the same and just a matter of style. I am somehow unable to get my head around this.

    DbContext is a UoW in itself, so what would be the benefits the having a separate UoW ?

    @ashutoshraina possibly to abstract the UoW itself. People do that thinking they're going to have to change their ORM or data storage eventually. In my experience, it never happens and thus the less you abstract the better.

    It's generally better to mock an interface which doesn't have dependencies on libraries other than the BCL itself. Even if you won't change your ORM, your tests can run without depending on anything but the BCL. Hence the need to wrap DbContext. Won't be much anyway. To make a point, we did this for EF 1 combined with the EF Poco Adapter project from codeplex. When migrating to EF 4, we had to swap out the old contexts and dbset implementations for the new ones, and benefited from having our own UOW and Repos.

    @Lars-Erik See my Edit 2.

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM