Unit of work Repository pattern DBcontext EF-Entities and ninject

Posted on

Problem

I am totally confused while implementing Repository pattern with unit of work and Entity framework because I have seen tons of examples. Please suggest whether I am on the right track or not.

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

Here, Medevenet is EF DBContext. For each repo I have property in Unit or Work classes.

public class UnitOfWork : IUnitOfWork
{
    private MedevnetDBEntities context;

    private PostRepository postRepository;

    public UnitOfWork(string connectionString)
    {
        context = new MedevnetDBEntities(connectionString);
    }


    public PostRepository PostRepository
    {
        get
        {
            if (this.postRepository == null)
            {
                this.postRepository = new PostRepository(context);
            }
            return postRepository;
        }
    }

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

    private bool disposed = false;
    private string p;

    public virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

public interface IRepository<T> where T : class
{
    void Insert(T entity);
    void Delete(object id);
    void Delete(T entityToDelete);
    void Update(T entityToUpdate);
}

 public class Repository<T> : IRepository<T> where T : class
{
    protected MedevnetDBEntities context;
    internal DbSet<T> dbSet;

    public Repository(MedevnetDBEntities context)
    {
        this.context = context;
        this.dbSet = context.Set<T>();
    }
    public virtual IEnumerable<T> GetWithRawSql(string query, params object[] parameters)
    {
        return dbSet.SqlQuery(query, parameters).ToList();
    }

    public virtual IEnumerable<T> Get(
        Expression<Func<T, bool>> filter = null,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<T> query = dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split
            (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }

    public virtual T GetByID(object id)
    {
        return dbSet.Find(id);
    }

    public virtual void Insert(T entity)
    {
        dbSet.Add(entity);
    }

    public virtual void Delete(object id)
    {
        T entityToDelete = dbSet.Find(id);
        Delete(entityToDelete);
    }

    public virtual void Delete(T entityToDelete)
    {
        if (context.Entry(entityToDelete).State == EntityState.Detached)
        {
            dbSet.Attach(entityToDelete);
        }
        dbSet.Remove(entityToDelete);
    }

    public virtual void Update(T entityToUpdate)
    {
        dbSet.Attach(entityToUpdate);
        context.Entry(entityToUpdate).State = EntityState.Modified;
    }
}

Ninject dependency config:

 private static void RegisterServices(IKernel kernel)
    {
        //kernel.Bind<MedevnetDBEntities>().ToSelf().InRequestScope();
        kernel.Bind<System.Data.Entity.DbContext>().To<MedevnetDBEntities>().InRequestScope().WithConstructorArgument("connectionString", ApplicationDomainConfiguration.ConnectionString);
        kernel.Bind<IUnitOfWork>().To<UnitOfWork>().WithConstructorArgument("connectionString", ApplicationDomainConfiguration.ConnectionString);
        kernel.Bind<IPostRepository>().To<PostRepository>().WithConstructorArgument(new MedevnetDBEntities(ApplicationDomainConfiguration.ConnectionString)); 
        //PostRepository
    }        

Solution

  • IUnitOfWork

    • as IDisposable is only relevant for an instance of a class, it shouldn’t be implemented by the IUnitOfWork interface. This would only add an unnecessary constraint to the interface.

    • Dispose(bool) shouldn’t be there. This is a method which should only exist in the class and also should be protected. By using it in an interface it automatically will be public.

  • UnitOfWork

    • dead code should be removed (here private string p;)
    • if you use this you should be consistent (see coonstructor and PostRepository property)
  • IRepository<T>

    • the names of the input parameters aren’t consistent. I suggest changing entityToDelete and entityToUpdate to entity as the action which should happen is expressed in the method name and therefor shouldn’t be reflected in the parameter name.
  • Repository<T>.Get()

    • you could make your code more readable, by extracting the Split() out of the loop definition.

    • you can remove the last else, as this is not needed at all.

    public virtual IEnumerable<T> Get(
        Expression<Func<T, bool>> filter = null,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<T> query = dbSet;
    
        if (filter != null)
        {
            query = query.Where(filter);
        }
    
        foreach (var includeProperty in includeProperties.Split
            (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }
    
        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    } 
    

    will become

    public virtual IEnumerable<T> Get(
        Expression<Func<T, bool>> filter = null,
        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<T> query = dbSet;
    
        if (filter != null)
        {
            query = query.Where(filter);
        }
    
        var properties = includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
    
        foreach (var includeProperty in properties)
        {
            query = query.Include(includeProperty);
        }
    
        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
    
        return query.ToList();
    }
    

Normally I do not recommend repository pattern and unit of work pattern with entity Framework. The reason is DBContext itself is a Unit of Work and DBSet itself is a repository. So there is no point that implementing the same thing again if there isn’t any very specific reason.

Then how do we achieve re-usability?

The answer is you can use extension methods instated repository method. Let’s say you want to filter by FirstName in several places in you business logic. Then you can add following extension method.

public static IEnumerable<Person> FirstNameEqualTo(this DbSet<Person> person , string firstName)
    {
        return person.Where(a=>a.FirstName=firstName).ToList();
    }

Then can call this method

DbContext.People.FirstNameEqualTo("Milina Udara");

With this concept you can remove all the code you wrote.

It is ok to write code even without extensions for smaller queries like above. you can use extensions only for lager queries and queries that are used in many places. However, most of the time filters like above needed only in one place.

How do we use this for data saving ?

You may receive ViewModel form the request but end of the day you need a model object to save. So converting ViewModel to model or model to ViewModel is a different responsibility. For that you can use auto-mapper, Service layer, or Model/ViewModel extensions. Then save those object using entity framework.

If you have two or many database calls form a method or controller action you can add services layer.

How do we unit test this?

You can mock DbSet.

The main advantage was unit testing, but with entity framework 6 onward you can mock DbSet

If you check StudentRepository every method other than DeleteStudent has only one line of code.

Leave a Reply

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