Saturday, March 12, 2016

Repository and Unit of Work pattern and why we need them

There are so many articles, blog posts regarding Repositories and Unit of work. But many of them are conflicting with each other. Therefore In this article I’m going to explain simply about the  Repository and  Unit of Work pattern  and why we need them with a sample project .

What is Repository?
By Martin Fowler (Patterns of Enterprise Architecture Book):
Mediates between the domain and data mapping layers, acting like an in memory collection of domain objects.

Why We Need?
Major benefits of Repository Patterns:
1.       Decouples your application from persistence framework (in this case Entity Framework)
                Approximately every Two years we get new persistence framework.
 First we have ADO.NET,
LINQ TO SQL,
EF V1,
nHibernate,
EF4,
EF 4.1(Db Context),
Now we have EF 7
If you want to have a freedom for changing persistence frame work with minimal impact of the application then you should use the repository pattern.
So In the future if you decide to change to persistence framework we can do so with minimal impact for rest of the application. Today we can use EF as repository, tomorrow we can change to stored procedure due to optimization.

2.  Minimize duplicate query logic
Imagine in few different places in our application we need to get top 10  books in a given category.
Without the repository we duplicate following code over and over again in our application.

var topTenBooks =context.Items
.Where(x.some Condition)
.OrderByDesending(x. Id)
.Take(10)

In situation like this we can encapsulate that logic in repository and simply call method like getTopTenBooks () as follows.

var books=repository. getTopTenBooks ();

What is the repository in practice?
In a nutshell this is what your repository look like. It should act like a collection of object in memory.so that we should have method to add and remove object in memory,get all object and get an object per id and find object using predicate.
·         Add(obj)
·         Remove(obj)
·         Get(id)
·         GetAll()
·         Find(predicate)

Implementing Repository



Note that here we don’t have method call update () and save ().Because in collection of memory(Repository) if you want to update an object simply get it from collection and change it. We don’t tell to collection to update and object.

Ex: if you want to update name of the book in collection of memory
var Book = collection.Get(1);
Book.Name=”New Name”;

Then the next question is How are we going to save these objects to Database?
 That’s when Unit Of Work pattern comes with repository. According to Martin Fowler (Patterns of Enterprise Architecture Book):
Unit of Work maintains a list of objects affected by a business transaction and coordinates the writing out of changes
  
 Implementing Unit of Work
 


Sample Code:
Here I have created demo project to implement repositories and unit of work pattern,Imagine you have Author table with colom id and name.And also Book table with id ,name and authorId as a foreign key.IRepository,Repository are generic repositories and IBookrepository and BookRepository are custormize repositories.
Ø  IRepository interface like collection of the object in memory.it has methods like below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Demo.Core.Repositories
{
    public interface IRepositories<TEntity> where TEntity : class
    {
        TEntity Get(int id);
        IEnumerable<TEntity> GetAll();
        IEnumerable<TEntity>Find(Expression<Func<TEntity, bool>>predicate);

        void Add(TEntity entity);
        void AddRange(IEnumerable<TEntity>entities);

        void Remove(TEntity entity);
        void ReomveRange(IEnumerable<TEntity>entities);
    }
}


Ø  Repository class is an implement of the IRepository interface.in side this we have generic DbContext.
using Demo.Core.Repositories;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Web;

namespace Demo.Persistence.Repositories
{
    public class Repository<TEntity>:IRepositories<TEntity> where TEntity : class
    {
        protected readonly DbContext Context;

        public Repository(DbContext context)
        {
            Context = context;
        }

        public TEntity Get(int id)
        {
        return Context.Set<TEntity>().Find(id);
        }
        public IEnumerable<TEntity> GetAll()
        {
        return Context.Set<TEntity>().ToList();
        }
        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
        {
        return Context.Set<TEntity>().Where(predicate);
        }
        public void Add(TEntity entity)
        {
         Context.Set<TEntity>().Add(entity);
        }
        public void AddRange(IEnumerable<TEntity>entities)
        {
         Context.Set<TEntity>().AddRange(entities);
        }
        public void Remove(TEntity entity)
        {
         Context.Set<TEntity>().Remove(entity);
        }
        public void ReomveRange(IEnumerable<TEntity>entities)
        {
            Context.Set<TEntity>().RemoveRange(entities);
        }
    }
}


Ø  IBookRepository Interface we define any operation that not in generic. This is customize repository.
Ex: GetTopTenBooks(),GetBookwithAuthors()
using Demo.Core.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Demo.Core.Repositories
{
    public interface IBookRepository :IRepositories<Book>
    {
        IEnumerable<Book> GetTopTenBooks();
        IEnumerable<Book> GetBookwithAuthors();
    }
}

Ø  BookRepository derives from Repository and additionally implement IBookRepository interface.
using Demo.Core.Domain;
using Demo.Core.Repositories;
using Demo.Persistence.Repositories;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace Demo.Persistence.Repositories
{
    public class BookRepository : Repository<Book>, IBookRepository
    {
        public BookRepository(DemoContext context)
        : base(context)
        {
        }
        IEnumerable<Book> GetTopTenBooks()
        {
            return DemoContext.Books.Take(10).ToList();
           
        }

        public IEnumerable<Book> GetCoursesWithAuthors()
        {
            return DemoContext.Books
                              .Include(c => c.Author)
                              .OrderBy(c => c.Name)
                              .ToList();
        }

        public DemoContext DemoContext
        {
            get { return Context as DemoContext; }
        }

    }

  
}

Implementing Unit of Work
Unit of Work is specific to our application and going to expose various repository based on the entities we have.


Ø  IUnitOfWork:
using Demo.Core.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Demo.Core
{
    interface IUnitOfWork :IDisposable
    {
        IBookRepository Courses { get; }
        IAuthorRepository Authors { get; }
        int Complete();
    }
}




Ø  UnitOfWork:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Demo.Core;
using Demo.Core.Repositories;
using Demo.Persistence.Repositories;


namespace Demo.Persistence
{
    public class UnitOfWork:IUnitOfWork
    {
        private readonly DemoContext _context;

        public UnitOfWork(DemoContext context)
        {
            _context = context;
            Books = new BookRepository(_context);
            Authors = new AuthorRepository(_context);
        }

        public IBookRepository Books { get; private set; }
        public IAuthorRepository Authors { get; private set; }
    }

}



That’s all. We completed repository and unit of work implementation.
So let’s try to get Top Ten books using MVC project. I won’t explain MVC implementation step by step. That is out of the scope.In brief what I did was first adding database using ado.net Entitiy data model (Db first) and naming context as ‘DemoContext’.Then adding BookContrller as follow.

using Demo.Core.Domain;
using Demo.Persistence;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Demo.Controllers
{
    public class BookController : Controller
    {
        private readonly UnitOfWork _UOWork;
        public BookController()
        {
            _UOWork = new UnitOfWork(new DemoContext());

        }
  
        // GET: Book
        public ActionResult Index()
        {
           var bookwitauthor= _UOWork.Books.GetBooksWithAuthors();
           return View(bookwitauthor);
        }

        public ActionResult getTopTenBooks()
        {
            //GET TOP 10 BOOKS from Book entity
            var topSelling = _UOWork.Books.getTopTenBooks();
            return View(topSelling);
        }

    }
}


Then right click on getTopTenBooks() method and add view as follow.

@model IEnumerable<Demo.Core.Domain.Book>

@{
    ViewBag.Title = "getTopTenBooks";
}

<h2>getTopTenBooks</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Description)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Author.Name)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Description)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Author.Name)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
    </tr>
}

</table>

After that when you call Book controller index method it will show book list as follow.

1 comment: