Implement Repository pattern in .Net

 

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

  1. Introduction
  2. Why use Repository
  3. Misconceptions
  4. Implementation
  5. Conclusion
  6. 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, How your data is persisted, It may be using some database server like MySQL, Oracle or it may be 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.

  1. Loosely coupled: To make the application loosely coupled and remove the specific database dependency from the application.
  2. Unit Testing: Increase testability of application, there are so many frameworks available to mock the repositories to write the unit test cases.
  3. 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.
  4. 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 places.
  5. 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 technologies then you should be the perfect applicant for implementing the repository pattern.

Misconceptions about repository pattern

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

  • A repository is a replacement for data access layer.
  • Implementing the repository in right way.
  • Repository pattern should not be used if you are using  Entity Framework.

Implementation

Enough of talking now 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 entity framework DbContext.

RepoDemoProjectStructure

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

using 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 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);
        }
    }
}

Customer Controller class using ApplicationDbContext for performing all database operations.  Running this Demo application will work like charm no issue, I can Add, update and delete the customer.

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

  1. By seeing the code only we can identify there is no separation of concern.
  2. An application is tightly coupled with the Entity Framework.
  3. 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.
  4. Let’s say you want to add softDelete column for all the tables. with this approach, you have to this so 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 move the database related stuff to the different project and call that project as Repository.Demo.DAL.

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

RepoDemoDalProjectStructure.PNG

So let’s create the interface called IRepository. This interface usage generic parameter T that 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 requirement like adding methods for search method by Id, Name etc.

Now let’s Create the implementation’s class for IRepository interface. I am calling this class as EfCustomerRepository because this Repository is for customer Entity and we are using Entity Framework for backend database so Ef as 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 Reference the Repository.Demo.DAL project to the Web Project. And make change the CustomerController class to use IReposiroty instead of directly using ApplicationDbContext class.

After making changes in 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");
		}


	}
}

Great! Now you are using repository pattern in your application. However, there is a lot of scope for enhancement like implementing the Generic Repository, Repository with unitofwork and using the dependency injections in the application.  I am going to post more article 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.

Catch us on social media

Author Profile

Deependra Kushwah
Deependra Kushwah
Deependra is a Senior Developer with Microsoft technologies, currently working with Opteamix India business private solution. In My Free time, I write blogs and make technical youtube videos. Having the good understanding of Service-oriented architect, Designing microservices using domain driven design.

Deependra Kushwah

Deependra is a Senior Developer with Microsoft technologies, currently working with Opteamix India business private solution. In My Free time, I write blogs and make technical youtube videos. Having the good understanding of Service-oriented architect, Designing microservices using domain driven design.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

RSS
Facebook
Google+
https://betechnical.blog/2018/02/08/implement-repository-pattern-in-net">
Twitter
YouTube
Pinterest
Pinterest
LinkedIn
Instagram
%d bloggers like this: