Generic Repository Pattern C# Implementation

Over the past few years, I have been using the repository pattern in most of my applications that use the entity framework. I have been creating interfaces and classes for each model class, and implementing CRUD and extended queries in each repository class. Every project I think it would be better to use a generic base class to handle the CRUD operations and use inherited classed for the more specialized queries. Of course time constraints have always gotten in the way. I found a few articles on generic repositories, but none of them were quite the complete solution, so I took bits and pieces from each and put together a pretty decent generic repository implementation.
To use this implementation, you will start out with the generic class repository.cs. I put this in the MtuRepository namespace, so it would be easier to reuse in each project. I typically create a separate project for my data model and create an Models and Repositories directory, but this will also work if everything is in a single project. I put the repository.cs and the unitofwork.cs files in the repository folder.
One of the things I really like about the way this is implemented is that the Get method takes in lambda arguments for the filter and sorting parameter, and it also takes in a comma separated list for included properties, so following navigation properties is supported. I like to keep my interfaces and classes together in the same file for repository classes because it is easier to get to the definition when using visual studio.
The generic repository looks like this:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace com.kartech.Repository
{
    public interface IRepository<TEntity> where TEntity : class
    {
        IEnumerable<TEntity> Get(
                                Expression<Func<TEntity, bool>> filter = null,
                                Func<IQueryable<TEntity>,
                                IOrderedQueryable<TEntity>> orderBy = null,
                                string includeProperties = ""
                                );
 IEnumerable<TEntity> GetAll();
        IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
        TEntity Get(object Id);
        void Add(TEntity entity);
        void AddRange(IEnumerable<TEntity> entities);
        void Update(TEntity entity);
        void Remove(object Id);
        void Remove(TEntity entity);
        void RemoveRange(IEnumerable<TEntity> entities);
    }
    /// <summary>
    /// This class takes in a type and generates the CRUD actions for that type. 
    /// This class can be used by itself, but more likely will be inherited to add additional queries.
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        protected readonly DbContext db;
        public Repository(DbContext _db)
        {
            db = _db;
        }
        /// <summary>
        /// This function takes in three parameters.
        /// </summary>
        /// <param name="filter">a => a.Name.StartsWith("StartText")</param>
        /// <param name="orderBy">orderBy: q => q.OrderBy(d => d.Name)</param>
        /// <param name="includeProperties">Comma Separated related objects</param>
        /// <returns></returns>
        public virtual IEnumerable<TEntity> Get(
                   Expression<Func<TEntity, bool>> filter = null,
                   Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
                   string includeProperties = "")
        {
            IQueryable<TEntity> query = db.Set<TEntity>();

            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 IEnumerable<TEntity> GetAll()
        {
            return db.Set<TEntity>().ToList();
        }

        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
        {
            return db.Set<TEntity>().Where(predicate);
        }

        public TEntity Get(object Id)
        {
            return db.Set<TEntity>().Find(Id);
        }

        public void Add(TEntity entity)
        {
            db.Set<TEntity>().Add(entity);
        }

        public void AddRange(IEnumerable<TEntity> entities)
        {
            db.Set<TEntity>().AddRange(entities);
        }

        public void Remove(TEntity entity)
        {
            db.Set<TEntity>().Remove(entity);
        }

        public void RemoveRange(IEnumerable<TEntity> entities)
        {
            db.Set<TEntity>().RemoveRange(entities);
        }

        public void Remove(object Id)
        {
            TEntity entity = db.Set<TEntity>().Find(Id);
            this.Remove(entity);
        }

        public void Update(TEntity entity)
        {
            db.Entry(entity).State = EntityState.Modified;
        }
    }
}
For a lot of repositories, this class may be all you need. If you do need to do more custom operations in your repository, you just create a new class and inherit from the repository class. Here is an example of a class where the data model didn't have the navigation properties setup properly, and I had to do a join in the linq query.
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using SampleSite.Models;
using SampleSite.ViewModels;
using com.kartech.Repository;

namespace com.kartech.Repository
{
    public interface ICustomerRepository : IRepository<Customer>
    {
        IEnumerable<CustomerList> GetCustomerList();
    }
    public class CustomerRepository : Repository<Customer>, ICustomerRepository
    {
        public DbContext context
        {
            get
            {
                return db as DbContext;
            }
        }
        public CustomerRepository(DbContext _db) : base(_db)
        {
        }

        public IEnumerable<CustomerList> GetCustomerList()
        {
            //return db.Set<TEntity>().ToList();
            var list = (from a in context.Set<Customer>()
                        join d in context.Set<Sales>() on a.CustomerCode equals d.CustomerCode
                        select new CustomerList
                        {
                            Customer = a,
                            SalesOrderNum = d.SalesOrderNum
                        }).ToList();
            
            return list;
        }
    }
}
After creating the repositories, you will want to use the Unit of Work pattern to access them. I just create a unitofwork.cs class in the repository directory. The advantage of the unit of work pattern is that you can use the same db context for all your operations, then if one fails on the save you can roll back the other operations.
Here is an example of the unitofwork.cs class.
namespace com.kartech.Repository
{
    public interface IUnitOfWork : IDisposable
    {
        ISalesRepository Saless { get; }
        ICustomerInfoRepository CustomerInfos { get; }


        int SaveChanges();
    }

    /// <summary>
    /// The Unit of Work pattern allows you to share a single DbContext among several queries allow you to rollback if one fails. 
    /// For each repository, create a new entry in the interface and copy one of the get sections below changing the type information.
    /// </summary>
    public class UnitOfWork : IUnitOfWork
    {
        private readonly DbContext db;

        public UnitOfWork()
        {
            db = new SampleContext();
        }

        private ISalesRepository _Sales;
        public ISalesRepository Sales
        {
            get
            {
                if (this._Sales == null)
                {
                    this._Sales = new SalesRepository(db);
                }
                return this._Sales;
            }
        }

        private ICustomerInfoRepository _CustomerInfos;
        public ICustomerInfoRepository CustomerInfos
        {
            get
            {
                if (this._CustomerInfos == null)
                {
                    this._CustomerInfos = new CustomerInfoRepository(db);
                }
                return this._CustomerInfos;
            }
        }

        public int SaveChanges()
        {
            return db.SaveChanges();
        }

        public void Dispose()
        {
            db.Dispose();
        }
    }
}
Below is an example of how this works in the controller.
public ActionResult Index()
        {
            
            var unitOfWork = new UnitOfWork();

            var customerList = unitOfWork.CustomerInfos.GetCustomerList();
            
            return View(customerList);
        }
Using the unit of work pattern really helps in keeping the controller really lightweight and clean. In most cases I would create a service class that loads the unit of work and keep the controller clean of all data logic, but I put it in the index here just as an example.

Comments

Popular posts from this blog

Asp.Net Core with Extended Identity and Jwt Auth Walkthrough

File Backups to Dropbox with PowerShell

Dynamic Expression Builder with EF Core