Friday, December 2, 2016

Entity Framework Code First with Repository Pattern

Problem: Need to develop a back-end (Data Layer and Business Logic Layer) of a data-driven application with less effort. Both layers must be able to unit test.

Solution: Implement data model using Entity Framework with code first approach and implement Data access layer using generic repository pattern. On top of that, implement a unit of work pattern to create an abstraction layer in between the data access layer and the business logic layer.

Sample Code : 
Two class library-type projects have been added to the solution, One project contains the entity classes and the other project contains the model (Project reference has been added from the Entity project to the Model project). A code-first entity model is added to the model project and code-first entity classes are created in the entity project.

Ex: Sample code first entity class
using System.ComponentModel.DataAnnotations.Schema;
namespace MyDevTalks.Entities
{
    [Table("Customers")]
        public class Customer 
    {
        public Guid Id { get; set; }
      public string Name { get; set; }
    }
}

Sample Model class :
namespace MyDevTalks.Model
{
    using MyDevTalks.Entities;
    using System.Data.Entity;
    public class Context : DbContext
    {
        public Context() : base("name=DataModel")
        {
        }
        public virtual DbSet<Customer> Customers { get; set; }
    }
}

A generic interface class IGenericRepository<T> containing the CRUD operations that are common to all the entities in the model is added to the Model project.
using System;
using System.Linq;
using System.Linq.Expressions;
namespace MyDevTalks.Model
{
    public interface IGenericRepository<T> where T : class
    {
        IQueryable<T> GetAll();
        IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
        void Add(T entity);
        void Delete(T entity);
        void Edit(T entity);
        void Save();
    }
}

Then IGenericRepository<T> interface is implemented in a generic abstract class,
using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
namespace MyDevTalks.Model
{
    public abstract class GenericRepository<C, T> : 
         IGenericRepository<T> where T : class where C : DbContext, new()
    {
        private C entities = new C();
        public C Context
        {
            get { return entities; }
            set { entities = value; }
        }
        public virtual IQueryable<T> GetAll()
        {
            IQueryable<T> query = entities.Set<T>();
            return query;
        }
        public IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
        {
            IQueryable<T> query = entities.Set<T>().Where(predicate);
            return query;
        }
        public virtual void Add(T entity)
        {
            entities.Set<T>().Add(entity);
        }
        public virtual void Delete(T entity)
        {
            entities.Entry(entity).State = EntityState.Deleted;
            entities.Set<T>().Remove(entity);
        }
        public virtual void Edit(T entity)
        {
            entities.Entry(entity).State = EntityState.Modified;
        }
        public virtual void Save()
        {
            entities.SaveChanges();
        }
    }
}

Then, made some concrete repository classes implementing the abstract GenericRepository.
using MyDevTalks.Entities;
namespace MyDevTalks.Model.Repositories
{
    public class CustomerRepository : GenericRepository<Context, Customer>
    {
        public void Foo(Guid Id){}
    }
}

Now I'm able to do all the CRUD operations + any concrete implementation.

[TestMethod]
public void SomeTestMethod()
{
   // Dependancy inject CustomerRepository and ItemRepository
   Context myDbContext = new Context();
   CustomerRepository _customerRepository= new CustomerRepository(myDbContext);
   var cust = new Customer() { Id = Guid.NewGuid(), Name = "My Customer" };

   _customerRepository.Add(cust);
   _customerRepository.Foo();
   _customerRepository.Save();
//Some Other Repo
   SomeOtherRepository _otherRepository = new SomeOtherRepository (myDbContext);
   _otherRepository .Foo(cust.Id);
_otherRepository .Save();
}

So the problem arises here when you have several repository classes, all of them will maintain their own instance of the Context class, which could result in data inconsistency, So usually the context object parsing is implemented at the Unit of Work classes, but it's better to make the default constructor of the Concrete Repository classes to private and implement an overloaded constructor, which accepts the Context. Also, Save() method has been removed from both IGenericRepository interface and GenericRepository abstract class.

using MyDevTalks.Entities;
namespace MyDevTalks.Model.Repositories
{
    public class CustomerRepository : GenericRepository<Context, Customer>
    {
        private CustomerRepository() { }
        public CustomerRepository(Context model)
        {
           base.Context = model;
        }
        public void Foo() { }
    }
}

Accordingly, my test method should be altered as below. So the context is shared in between the repositories used in a single unit, in other words, a Unit of Work. 

[TestMethod]
public void SomeTestMethod()
{
  Context myDbContext = new Context();
  CustomerRepository custRep = new CustomerRepository(myDbContext);
  custRep.Add(new Entities.Customer() { Id = Guid.NewGuid(), Name = "My Customer" });
  custRep.Foo();
  //Some Other Repo
  SomeOtherRepository othrtRepo = new SomeOtherRepository (myDbContext);
  othrtRepo.Foo(custRep.Id);
  myDbContext.SaveChanges();
}