Repository and Unit Of Work Design Pattern– ASP.NET MVC


Hello! There are two design patterns that I’m going to speak about today, which have been covered loads online and in various books, but I’m going to cover them quickly here:

 

Repository Pattern

 

Quite simply this is a pattern to separate all the data access logic from your application into separate container classes. It is good practise to create Repositories for each Domain area of your application and separate data access concerns, like CRUD operations within these. For e.g. I have the following Domains within my application Accountable (A personal Accounts App):

 

  • LedgerUser – User details
  • LedgerPeriod – Date from/to for each Accounting period defined by the LedgerUser
  • LedgerTransaction – Ledger Transactions that occur each Ledger Period.
  • Many more….

‘I’m not going to go into massive detail, but each Repository of each domain would have the following methods which are generally found in data access:

 

  • Create – Create new records
  • Update – Update existing records
  • Delete – Delete existing records
  • Find – Find records

Do design your Data Source correctly then apply the Repository classes to these. So this should make you think There is a lot of duplicate code here…..”  What do we do in these cases? We write some generic Interfaces to impose some rules on how our Repositories should behave. Please raise your classes (get it…?!) to IRepository:

 

public interface IRepository<TEntity> 
{
    void Create(TEntity entity);
    void Update(TEntity entity);
    void Delete(TEntity entity);
    IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
    IQueryable<TEntity> FindAll();
    TEntity FindById(Guid id);
    

}

 

 

See we have used IQueryable so that we can use other awesome chained LINQ methods (like OrderBy).

 

So this all lovely, but now onto the nitty gritty, taking that interface and applying it to a concrete example. Because I’m using EF a lot at the moment, i have written a EntityFrameworkRepository example using DbContext and Dbset:

 

    public class EntityFrameworkRepository<T> : IRepository<T> 
        where T : class, IRepositoryEntity
    {
        internal DbContext context;
        internal DbSet<T> dbSet;

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="context"></param>
        public EntityFrameworkRepository(DbContext context)
        {
            this.context = context;
            this.dbSet = context.Set<T>();
        }

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

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

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

        }

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

        public T FindById(Guid id)
        {
            
            return dbSet.Single(o => o.id == id);
        }

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


        
    }
}

 

As you can see this class also implements IRepositoryEntity, which is an idea I stole off the Pluralsight video I watched, whereby in our IRepository interface where have a FindById method. This is presuming that the Entity in question using id within its design, which all my Entities do, so I’m safe to enforce this:

 

 

public interface IRepositoryEntity
{

    Guid id { get; }

}

 

 

 

Unit of Work

 

 

This is all nice and lovely but where is the data persistence? If you look closely and without just copy & pasting these examples, you will see in the Repository interfaces and concrete implementations, there are “commit” or “save” methods. Why? Well we can abstract this further into a Unit Of Work pattern which will:

  • Handle persistence
  • Share the context within Repositories

This will allow one context to be used without the Repositories having to know about each other or their respective Dbcontext’s. Now because we are good programmers we should first design the interface, IUnitOfWork:

public interface IUnitOfWork : IDisposable
{


    void Save();


}

 

Is that it?! Well yes. What i would like to do in the future is have a GenericCollectionRepository within the Unit Of Work concrete implementations, but i will refactor that in at a later date. So following the pattern of EF, this is the EntityFrameworkUnitOfWork:

 

public class EntityFrameworkUnitOfWork : IUnitOfWork
    {
        private AccountsContext context = new AccountsContext();
        internal IRepository<LedgerAccount> account;
        internal IRepository<LedgerUser> user;
        internal IRepository<UserType> userType;
        internal IRepository<Image> image;
        internal IRepository<SystemReference> systemReference;
        internal IRepository<LedgerService> ledgerService;

       


        public IRepository<LedgerAccount> Account
        {
            get
            {
                if (account == null)
                {
                    account = new 
EntityFrameworkRepository<LedgerAccount>(context); } return account; } } public IRepository<LedgerUser> User { get { if (user == null) { user = new EntityFrameworkRepository<LedgerUser>(context); } return user; } } public IRepository<UserType> UserType { get { if (userType == null) { userType = new EntityFrameworkRepository<UserType>(context); } return userType; } } public IRepository<Image> Image { get { if (image == null) { image = new EntityFrameworkRepository<Image>(context); } return image; } } public IRepository<SystemReference> SystemReference { get { if (systemReference == null) { systemReference = new
EntityFrameworkRepository<SystemReference>(context); } return systemReference; } } public IRepository<LedgerService> LedgerService { get { if (ledgerService == null) { ledgerService = new
EntityFrameworkRepository<LedgerService>(context); } return ledgerService; } } public void Save() { context.SaveChanges(); } private bool disposed = false; protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { context.Dispose(); } } this.disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }

The beautify of this approach is the reduction in business logic within your Controller of your MVC Application and reducing "controller bloat”.

 

Here is an example of the both patterns working in harmony:

 

 

public class LedgerUserController : Controller
   {
             
       private EntityFrameworkUnitOfWork unitOfWork = 
new EntityFrameworkUnitOfWork(); // // GET: /LedgerUser/Details/5 public ActionResult Details(Guid id) { LedgerUserViewModel viewModel = new LedgerUserViewModel(); UserType userType = new UserType(); try { viewModel.LedgerUser = unitOfWork.User.FindById(id); viewModel.UserType = unitOfWork.UserType.FindAll() .SingleOrDefault
(u => u.id == viewModel.LedgerUser.UserTypeId) .Description; viewModel.UniqueKey = viewModel.LedgerUser.id.ToString(); return View(viewModel); } catch (ArgumentNullException ex) { Console.WriteLine("ArgumentNullException " + ex.ToString()); } catch (InvalidOperationException ex) { Console.WriteLine("InvalidOperationException " + ex.ToString()); } return RedirectToAction("Index", "Home"); } }

 

All my controller is doing is what is was designed to, setting up the views data ready for rendering.

 

I know the code on these sites isnt the best, but i want to create a github repo of it soon. Watch this space! Smile

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Enhanced by Zemanta

3 thoughts on “Repository and Unit Of Work Design Pattern– ASP.NET MVC

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s