The repository pattern, also known as the repository design pattern, acts as a bridge between your data access layer and the business layer. Instead of interacting with the database directory, your application should use repositories to perform database-related operations.

Benefits of Using the Repository Pattern

There are a few benefits to using the repository pattern.

  • Loosely coupled:  To make the application loosely coupled and remove the specific database dependency from the application.
  • Unit Testing: Increase test-ability 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 a 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 operation 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 your application has more than one persistence store then your application is the ideal candidate for implementing the repository pattern.

How to Implement the Repository Pattern?

Enough of talking, let’s jump to the practical or demo application. I will be using the 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 the entity framework DbContext.

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

sing System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Repository.Demo.Models
{
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
    }
}

Now let’s Create the CustomerController class which will have all crud operations for the customer model class.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using Repository.Demo.Models;
namespace Repository.Demo.Controllers
{
    public class CustomersController : Controller
    {
        private ApplicationDbContext db = new ApplicationDbContext();
        // GET: Customers
        public ActionResult Index()
        {
            return View(db.Customers.ToList());
        }
        // GET: Customers/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Customer customer = db.Customers.Find(id);
            if (customer == null)
            {
                return HttpNotFound();
            }
            return View(customer);
        }
        // GET: Customers/Create
        public ActionResult Create()
        {
            return View();
        }
        // POST: Customers/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "Id,Name,Address")] Customer customer)
        {
            if (ModelState.IsValid)
            {
                db.Customers.Add(customer);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(customer);
        }
        // GET: Customers/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Customer customer = db.Customers.Find(id);
            if (customer == null)
            {
                return HttpNotFound();
            }
            return View(customer);
        }
        // POST: Customers/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "Id,Name,Address")] Customer customer)
        {
            if (ModelState.IsValid)
            {
                db.Entry(customer).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(customer);
        }
        // GET: Customers/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Customer customer = db.Customers.Find(id);
            if (customer == null)
            {
                return HttpNotFound();
            }
            return View(customer);
        }
        // POST: Customers/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Customer customer = db.Customers.Find(id);
            db.Customers.Remove(customer);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

The Customer Controller class uses ApplicationDbContext for performing all database operations. Running this demo application will work like a charm with no issues. I can add, update, and delete the customer.

We can easily identify the issues in the above code.

  • Not following the SOP(Separation of Concerns) principle
  • Tight coupling

We can tune our solution using the repository design pattern.

Let’s divide the project into two parts: web and DAL. Move the database-related stuff to the different projects and call that project a repository. Demo.DAL.

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

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Repository.Demo.DAL
{
 public interface IRepository<T> where T : class
 {
  Get All objects
 IEnumerable GetAll();
 Get Object by Id
 T GetById(int id);
 Create the object
 void Create(T entity);
 Delete object
 void Delete(T entity);
 void Delete(int id);
 Update  the object
 void Update(T entity);
 }
}

As you can see in the IRepository interface, we have defined the commonly used crud operations methods. You can extend this as per your requirements, like adding methods for search methods by ID, name, etc.

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

using System;
using System.Collections.Generic;
using Repository.Demo.Entities;
using Repository.Demo.DAL.Entities;
namespace Repository.Demo.DAL
{
 public class EfCustomerRepository : IRepository<Customer>
 {
 ///
 ///
 /// ///
 public void Create(Customer entity)
 {
 using (ApplicationDbContext context = new ApplicationDbContext())
 {
 context.Customers.Add(entity);
 context.SaveChanges();
 }
 }
 ///
 ///
 ///  ///
 public void Delete(int id)
 {
 using (ApplicationDbContext context = new ApplicationDbContext())
 {
 var entity = context.Customers.Find(id);
 context.Customers.Remove(entity);
 context.SaveChanges();
 }
 }
 ///
 ///
 ///  ///
 public void Delete(Customer entity)
 {
 using (ApplicationDbContext context = new ApplicationDbContext())
 {
 context.Customers.Remove(entity);
 context.SaveChanges();
 }
 }
 ///
 ///
 ///
 ///  / public IEnumerable GetAll()
 {
 using (ApplicationDbContext context = new ApplicationDbContext())
 {
 return context.Customers;
 }
 }
 ///
 ///
 ///  ///
 ///
 public Customer GetById(int id)
 {
 using (ApplicationDbContext context = new ApplicationDbContext())
 {
 return context.Customers.Find(id);
 }
 }
 ///
 ///
 ///  ///
 public void Update(Customer entity)
 {
 using (ApplicationDbContext context = new ApplicationDbContext())
 {
 context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
 context.SaveChanges();
 }
 }
 }
}

Now refer to the Repository.Demo.DAL project in the Web Project. And change the CustomerController class to use IRepositoy instead of directly using the ApplicationDbContext class.

After making changes in the CustomerController class.

using System.Net;
using System.Web.Mvc;
using Repository.Demo.DAL;
using Repository.Demo.Entities;
namespace Repository.Demo.Controllers
{
 public class CustomersController : Controller
 {
 private readonly IRepository customerrepo;
 public CustomersController()
 {
 //I am creating an Repository instance manualy,because i am not
 //using dependency injection in this demo.
 customerrepo = new EfCustomerRepository();
 }
 // GET: Customers
 public ActionResult Index()
 {
 return View(customerrepo.GetAll());
 }
 // GET: Customers/Details/5
 public ActionResult Details(int? id)
 {
 if (id == null)
 {
 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
 }
 Customer customer = customerrepo.GetById((int)id);
 if (customer == null)
 {
 return HttpNotFound();
 }
 return View(customer);
 }
 // GET: Customers/Create
 public ActionResult Create()
 {
 return View();
 }
 // POST: Customers/Create
 // To protect from overposting attacks, please enable the specific properties you want to bind to, for
 // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 [HttpPost]
 [ValidateAntiForgeryToken]
 public ActionResult Create([Bind(Include = "Id,Name,Address")] Customer customer)
 {
 if (ModelState.IsValid)
 {
 customerrepo.Create(customer);
 return RedirectToAction("Index");
 }
 return View(customer);
 }
 // GET: Customers/Edit/5
 public ActionResult Edit(int? id)
 {
 if (id == null)
 {
 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
 }
 Customer customer = customerrepo.GetById((int)id);
 if (customer == null)
 {
 return HttpNotFound();
 }
 return View(customer);
 }
 // POST: Customers/Edit/5
 // To protect from overposting attacks, please enable the specific properties you want to bind to, for
 // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 [HttpPost]
 [ValidateAntiForgeryToken]
 public ActionResult Edit([Bind(Include = "Id,Name,Address")] Customer customer)
 {
 if (ModelState.IsValid)
 {
 customerrepo.Update(customer);
 return RedirectToAction("Index");
 }
 return View(customer);
 }
 // GET: Customers/Delete/5
 public ActionResult Delete(int? id)
 {
 if (id == null)
 {
 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
 }
 Customer customer = customerrepo.GetById((int)id);
 if (customer == null)
 {
 return HttpNotFound();
 }
 return View(customer);
 }
 // POST: Customers/Delete/5
 [HttpPost, ActionName("Delete")]
 [ValidateAntiForgeryToken]
 public ActionResult DeleteConfirmed(int id)
 {
 customerrepo.Delete(id);
 return RedirectToAction("Index");
 }
 }
}

Conclusion

Now you are using the repository pattern in your application. However, there is a lot of scope for enhancement like implementing the Generic Repository, Repository with a 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.

I hope you found the article interesting. If you have any questions or comments about this article, please leave them in the comment section below.

82 / 100

1 Comment

Comments are closed