Implement Repository Pattern In .NET

The intention of this post is to explain about the Repository pattern and how it's useful in enterprise applications.

  • Introduction
  • Why use Repository
  • Misconceptions
  • Implementation
  • Conclusion
  • Source Code

Introduction

The repository pattern is an abstraction layer between your database and business layer. The idea behind implementing repository pattern is to hide the data persistence details. You data may be persisted by using some database server like MySQL or Oracle or it may be by using some flat file, Excel file or any NoSQL database like MongoDB or CouchDB etc.

Why use Repository

If you want to achieve the listed functionality in your application then you can go for the repository pattern.

  • Loosely coupled
    To make the application loosely coupled and remove the specific database dependency from the application.

  • Unit Testing
    Increase testability of application, there are so many frameworks available to mock the repositories to write the unit test cases.

  • Dependency Injection
    Utilize the power of dependency injections in your application. Since you are implementing an interface for the repository pattern, you can make use of DI container to create a repository object for you.

  • Clean Code
    Since you are using repositories for your database operations, you don’t need to spoil the code and write the database related operations all over the place.

  • Separation of Concern
    The repository also supports the target of accomplishing a clean separation and one-way dependency between the domain and data mapping layers.

If you have more then one persistence technology then you should be the perfect applicant for implementing the repository pattern.

Misconceptions about repository pattern

There are so many misconceptions among people about implementing the repository pattern.

A repository is a replacement for data access layer.

Repository pattern should not be used if you are using Entity Framework.

Implementation

Enough talking now; let’s jump into the practical or demo application. I will be using a simple C# application to demonstrate the repository pattern.

I have created an asp.net MVC application with the default template. In this application database, operations are performed inside the controller using entity framework DbContext.

Let’s create the Customer class with a few properties: Id, Name, and Address.

  1. using System.Collections.Generic;  
  2. using System.Linq;  
  3. using System.Web;  
  4. namespace Repository.Demo.Models {  
  5.     public class Customer {  
  6.         public int Id {  
  7.             get;  
  8.             set;  
  9.         }  
  10.         public string Name {  
  11.             get;  
  12.             set;  
  13.         }  
  14.         public string Address {  
  15.             get;  
  16.             set;  
  17.         }  
  18.     }  
  19. }  
Now, let’s create the CustomerController class which will have all CRUD operations for the customer model class.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Data;  
  4. using System.Data.Entity;  
  5. using System.Linq;  
  6. using System.Net;  
  7. using System.Web;  
  8. using System.Web.Mvc;  
  9. using Repository.Demo.Models;  
  10. namespace Repository.Demo.Controllers {  
  11.     public class CustomersController: Controller {  
  12.         private ApplicationDbContext db = new ApplicationDbContext();  
  13.         // GET: Customers  
  14.         public ActionResult Index() {  
  15.             return View(db.Customers.ToList());  
  16.         }  
  17.         // GET: Customers/Details/5  
  18.         public ActionResult Details(int ? id) {  
  19.             if (id == null) {  
  20.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  21.             }  
  22.             Customer customer = db.Customers.Find(id);  
  23.             if (customer == null) {  
  24.                 return HttpNotFound();  
  25.             }  
  26.             return View(customer);  
  27.         }  
  28.         // GET: Customers/Create  
  29.         public ActionResult Create() {  
  30.             return View();  
  31.         }  
  32.         // POST: Customers/Create  
  33.         // To protect from overposting attacks, please enable the specific properties you want to bind to, for  
  34.         // more details see http://go.microsoft.com/fwlink/?LinkId=317598.  
  35.         [HttpPost]  
  36.         [ValidateAntiForgeryToken]  
  37.         public ActionResult Create([Bind(Include = "Id,Name,Address")] Customer customer) {  
  38.             if (ModelState.IsValid) {  
  39.                 db.Customers.Add(customer);  
  40.                 db.SaveChanges();  
  41.                 return RedirectToAction("Index");  
  42.             }  
  43.             return View(customer);  
  44.         }  
  45.         // GET: Customers/Edit/5  
  46.         public ActionResult Edit(int ? id) {  
  47.             if (id == null) {  
  48.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  49.             }  
  50.             Customer customer = db.Customers.Find(id);  
  51.             if (customer == null) {  
  52.                 return HttpNotFound();  
  53.             }  
  54.             return View(customer);  
  55.         }  
  56.         // POST: Customers/Edit/5  
  57.         // To protect from overposting attacks, please enable the specific properties you want to bind to, for  
  58.         // more details see http://go.microsoft.com/fwlink/?LinkId=317598.  
  59.         [HttpPost]  
  60.         [ValidateAntiForgeryToken]  
  61.         public ActionResult Edit([Bind(Include = "Id,Name,Address")] Customer customer) {  
  62.             if (ModelState.IsValid) {  
  63.                 db.Entry(customer).State = EntityState.Modified;  
  64.                 db.SaveChanges();  
  65.                 return RedirectToAction("Index");  
  66.             }  
  67.             return View(customer);  
  68.         }  
  69.         // GET: Customers/Delete/5  
  70.         public ActionResult Delete(int ? id) {  
  71.             if (id == null) {  
  72.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  73.             }  
  74.             Customer customer = db.Customers.Find(id);  
  75.             if (customer == null) {  
  76.                 return HttpNotFound();  
  77.             }  
  78.             return View(customer);  
  79.         }  
  80.         // POST: Customers/Delete/5  
  81.         [HttpPost, ActionName("Delete")]  
  82.         [ValidateAntiForgeryToken]  
  83.         public ActionResult DeleteConfirmed(int id) {  
  84.             Customer customer = db.Customers.Find(id);  
  85.             db.Customers.Remove(customer);  
  86.             db.SaveChanges();  
  87.             return RedirectToAction("Index");  
  88.         }  
  89.         protected override void Dispose(bool disposing) {  
  90.             if (disposing) {  
  91.                 db.Dispose();  
  92.             }  
  93.             base.Dispose(disposing);  
  94.         }  
  95.     }  
  96. }  
Customer Controller class uses ApplicationDbContext for performing all database operations. Running this Demo application will work like a charm with no issue. I can add, update and delete the customer.

Now let’s talk about the problems we will face with this approach.

By seeing the code we can identify there is no separation of concern.

An application is tightly coupled with the Entity Framework.

If you want to move from Entity framework to some other ORM tool like NHibernate it’s going to be a big mess and will require a lot of time and code changes.

Let’s say you want to add softDelete column for all the tables. With this approach, you have to do this many times before you make any changes.

Let’s start with the repository pattern implementation to overcome these situations.

Let’s divide the project into two parts, web and DAL, and move the database related stuff to a different project and call that project Repository.Demo.DAL.

When it comes to the separation of concern the first thing that comes to mind is designing the contract or interface.

So let’s create the interface called IRepository. This interface uses a generic parameter that should be the class type. So you can implement the Repository for each of your entity classes.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading.Tasks;  
  6. namespace Repository.Demo.DAL {  
  7.     public interface IRepository < T > where T: class {  
  8.         ///  
  9.         /// Get All objects  
  10.         ///  
  11.         ///  
  12.         IEnumerable GetAll();  
  13.         ///  
  14.         /// Get Object by Id  
  15.         ///  
  16.         ///  
  17.         T GetById(int id);  
  18.         ///  
  19.         /// Create the object  
  20.         ///  
  21.         ///  
  22.         void Create(T entity);  
  23.         ///  
  24.         /// Delete object  
  25.         ///  
  26.         ///  
  27.         void Delete(T entity);  
  28.         ///  
  29.         ///  
  30.         ///  
  31.         ///  
  32.         void Delete(int id);  
  33.         ///  
  34.         /// Update the object  
  35.         ///  
  36.         ///  
  37.         void Update(T entity);  
  38.     }  
  39. }  
As you can see in the IRepository interface we have defined the commonly used CRUD operation methods.You can extend this as per your requirement like adding methods for searching method by Id, Name etc.

Now let’s create the implementation class for IRepository interface. I am calling this class EfCustomerRepository because this Repository is for customer Entity and we are using Entity Framework for the backend database so use Ef as the prefix.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using Repository.Demo.Entities;  
  4. using Repository.Demo.DAL.Entities;  
  5. namespace Repository.Demo.DAL {  
  6.     public class EfCustomerRepository: IRepository < Customer > {  
  7.         ///  
  8.         ///  
  9.         ///  
  10.         ///  
  11.         public void Create(Customer entity) {  
  12.             using(ApplicationDbContext context = new ApplicationDbContext()) {  
  13.                 context.Customers.Add(entity);  
  14.                 context.SaveChanges();  
  15.             }  
  16.         }  
  17.         ///  
  18.         ///  
  19.         ///  
  20.         ///  
  21.         public void Delete(int id) {  
  22.             using(ApplicationDbContext context = new ApplicationDbContext()) {  
  23.                 var entity = context.Customers.Find(id);  
  24.                 context.Customers.Remove(entity);  
  25.                 context.SaveChanges();  
  26.             }  
  27.         }  
  28.         ///  
  29.         ///  
  30.         ///  
  31.         ///  
  32.         public void Delete(Customer entity) {  
  33.             using(ApplicationDbContext context = new ApplicationDbContext()) {  
  34.                 context.Customers.Remove(entity);  
  35.                 context.SaveChanges();  
  36.             }  
  37.         }  
  38.         ///  
  39.         ///  
  40.         ///  
  41.         ///  
  42.         ///  
  43.         public IEnumerable GetAll() {  
  44.             using(ApplicationDbContext context = new ApplicationDbContext()) {  
  45.                 return context.Customers;  
  46.             }  
  47.         }  
  48.         ///  
  49.         ///  
  50.         ///  
  51.         ///  
  52.         ///  
  53.         public Customer GetById(int id) {  
  54.             using(ApplicationDbContext context = new ApplicationDbContext()) {  
  55.                 return context.Customers.Find(id);  
  56.             }  
  57.         }  
  58.         ///  
  59.         ///  
  60.         ///  
  61.         ///  
  62.         public void Update(Customer entity) {  
  63.             using(ApplicationDbContext context = new ApplicationDbContext()) {  
  64.                 context.Entry(entity).State = System.Data.Entity.EntityState.Modified;  
  65.                 context.SaveChanges();  
  66.             }  
  67.         }  
  68.     }  
  69. }  
Now Reference the Repository.Demo.DAL project to the Web Project. And make changes to the CustomerController class to use IReposiroty instead of directly using ApplicationDbContext class.

After making changes in CustomerController class:

  1. using System.Net;  
  2. using System.Web.Mvc;  
  3. using Repository.Demo.DAL;  
  4. using Repository.Demo.Entities;  
  5. namespace Repository.Demo.Controllers {  
  6.     public class CustomersController: Controller {  
  7.         private readonly IRepository customerrepo;  
  8.         public CustomersController() {  
  9.             //I am creating an Repository instance manualy,because i am not  
  10.             //using dependency injection in this demo.  
  11.             customerrepo = new EfCustomerRepository();  
  12.         }  
  13.         // GET: Customers  
  14.         public ActionResult Index() {  
  15.             return View(customerrepo.GetAll());  
  16.         }  
  17.         // GET: Customers/Details/5  
  18.         public ActionResult Details(int ? id) {  
  19.             if (id == null) {  
  20.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  21.             }  
  22.             Customer customer = customerrepo.GetById((int) id);  
  23.             if (customer == null) {  
  24.                 return HttpNotFound();  
  25.             }  
  26.             return View(customer);  
  27.         }  
  28.         // GET: Customers/Create  
  29.         public ActionResult Create() {  
  30.             return View();  
  31.         }  
  32.         // POST: Customers/Create  
  33.         // To protect from overposting attacks, please enable the specific properties you want to bind to, for  
  34.         // more details see http://go.microsoft.com/fwlink/?LinkId=317598.  
  35.         [HttpPost]  
  36.         [ValidateAntiForgeryToken]  
  37.         public ActionResult Create([Bind(Include = "Id,Name,Address")] Customer customer) {  
  38.             if (ModelState.IsValid) {  
  39.                 customerrepo.Create(customer);  
  40.                 return RedirectToAction("Index");  
  41.             }  
  42.             return View(customer);  
  43.         }  
  44.         // GET: Customers/Edit/5  
  45.         public ActionResult Edit(int ? id) {  
  46.             if (id == null) {  
  47.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  48.             }  
  49.             Customer customer = customerrepo.GetById((int) id);  
  50.             if (customer == null) {  
  51.                 return HttpNotFound();  
  52.             }  
  53.             return View(customer);  
  54.         }  
  55.         // POST: Customers/Edit/5  
  56.         // To protect from overposting attacks, please enable the specific properties you want to bind to, for  
  57.         // more details see http://go.microsoft.com/fwlink/?LinkId=317598.  
  58.         [HttpPost]  
  59.         [ValidateAntiForgeryToken]  
  60.         public ActionResult Edit([Bind(Include = "Id,Name,Address")] Customer customer) {  
  61.             if (ModelState.IsValid) {  
  62.                 customerrepo.Update(customer);  
  63.                 return RedirectToAction("Index");  
  64.             }  
  65.             return View(customer);  
  66.         }  
  67.         // GET: Customers/Delete/5  
  68.         public ActionResult Delete(int ? id) {  
  69.             if (id == null) {  
  70.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
  71.             }  
  72.             Customer customer = customerrepo.GetById((int) id);  
  73.             if (customer == null) {  
  74.                 return HttpNotFound();  
  75.             }  
  76.             return View(customer);  
  77.         }  
  78.         // POST: Customers/Delete/5  
  79.         [HttpPost, ActionName("Delete")]  
  80.         [ValidateAntiForgeryToken]  
  81.         public ActionResult DeleteConfirmed(int id) {  
  82.             customerrepo.Delete(id);  
  83.             return RedirectToAction("Index");  
  84.         }  
  85.     }  
  86. }  

Great! Now you are using repository pattern in your application. However, there is a lot of room for enhancement like implementing the generic repository, repository with the unit of work, and using the dependency injections in the application. I am going to post more articles as the continuation of this repository pattern implementation where we will try to achieve all these enhancements.

Source Code

Click here Git to download the Entire source code of this demo application.