Back to: Design Patterns in C# With Real-Time Examples
Using Both Generic and Non-Generic Repository Pattern in C#
In this article, I am going to discuss how to implement both generic and non-generic repository patterns in ASP.NET MVC applications using Entity Framework. In most real-time applications, the generic repository contains the methods which are common for all the entities. But if you want some specific operation for some specific repository. Then you need to create a specific repository with the required operations. Here, in this article, I will show you how to implement both generic and specific repositories for an entity.
As we already discussed the repository pattern is used to create an abstraction layer between the data access layer and business logic layer to perform the CRUD operations against the underlying database. We also discussed that the repository design pattern can be implemented in the following two ways. So, I strongly recommended you read the following two articles before proceeding to this article.
Generic Repository Pattern
The generic repository pattern is used to define common database operations such as Create, Retrieve, Update, Delete, etc. for all the database entities in a single class.
Non-Generic Repository Pattern (Specific Repository)
The non-generic repository pattern is used to define all database operations related to a specific entity within a separate class. For example, if you have two entities let’s say, Employee and Customer, then each entity will have its own implementation repository.
So, before implementing both generic and specific repositories let us first understand the implementation guidelines. That means when to use Generic and when to use Specific and when to use both generic and specific in an application.
Repository Pattern Implementation Guidelines
If you will use one of the above implementations, then with the generic repository, you cannot use specific operations for an entity and in the case of non-generic implementation, you have to write code for common CRUD operations for each entity. So better way is, just to create a generic repository for commonly used CRUD operations, and for specific operations create a non-generic repository and inherit from the generic repository. The below diagram explains the above things.
Let’s understand this with one example
We are going to work with the same example that we started in the Non-Generic Repository Design pattern, and Continue in Generic Repository Design Pattern articles. So please read these two articles before proceeding to this article. Modify the IGenericRepository.cs file as shown below.
namespace RepositoryUsingEFinMVC.GenericRepository { public interface IGenericRepository<T> where T : class { IEnumerable<T> GetAll(); T GetById(object id); void Insert(T obj); void Update(T obj); void Delete(object id); void Save(); } }
Modify the GenericRepository.cs file as shown below
namespace RepositoryUsingEFinMVC.GenericRepository { public class GenericRepository<T> : IGenericRepository<T> where T : class { public EmployeeDBContext _context = null; public DbSet<T> table = null; public GenericRepository() { this._context = new EmployeeDBContext(); table = _context.Set<T>(); } public GenericRepository(EmployeeDBContext _context) { this._context = _context; table = _context.Set<T>(); } public IEnumerable<T> GetAll() { return table.ToList(); } public T GetById(object id) { return table.Find(id); } public void Insert(T obj) { table.Add(obj); } public void Update(T obj) { table.Attach(obj); _context.Entry(obj).State = EntityState.Modified; } public void Delete(object id) { T existing = table.Find(id); table.Remove(existing); } public void Save() { _context.SaveChanges(); } } }
Note: The above code is the implementation of the Generic Repository where we implement the code for common CRUD operation for each entity.
Now we need to provide a specific implementation for each entity. Let say we want two extra operations for the Employee entity such as to get Employees By gender and get employees by department. As these two operations are specific to the Employee entity there is no point to add these two operations in the Generic Repository.
So we need to create a non-generic repository called EmployeeRepository which will also inherit from GenericRepository. Within this repository, we need to provide the two specific operations as shown below.
Modify the IEmployeeRepository.cs file as shown below.
using RepositoryUsingEFinMVC.DAL; using RepositoryUsingEFinMVC.GenericRepository; using System.Collections.Generic; namespace RepositoryUsingEFinMVC.Repository { public interface IEmployeeRepository : IGenericRepository<Employee> { IEnumerable<Employee> GetEmployeesByGender(string Gender); IEnumerable<Employee> GetEmployeesByDepartment(string Dept); } }
Modify the EmployeeRepository.cs file as shown below
namespace RepositoryUsingEFinMVC.Repository { public class EmployeeRepository : GenericRepository<Employee>, IEmployeeRepository { public IEnumerable<Employee> GetEmployeesByGender(string Gender) { return _context.Employees.Where(emp => emp.Gender == Gender).ToList(); } public IEnumerable<Employee> GetEmployeesByDepartment(string Dept) { return _context.Employees.Where(emp => emp.Dept == Dept).ToList(); } } }
Now we need to use both these generic and non-generic repositories in Employee Controller.
Modify the Employee Controller as shown below.
using RepositoryUsingEFinMVC.Repository; using System.Web.Mvc; using RepositoryUsingEFinMVC.DAL; using RepositoryUsingEFinMVC.GenericRepository; namespace RepositoryUsingEFinMVC.Controllers { public class EmployeeController : Controller { private IGenericRepository<Employee> repository = null; private IEmployeeRepository employee_repository = null; public EmployeeController() { this.employee_repository = new EmployeeRepository(); this.repository = new GenericRepository<Employee>(); } public EmployeeController(EmployeeRepository repository) { this.employee_repository = repository; } public EmployeeController(IGenericRepository<Employee> repository) { this.repository = repository; } [HttpGet] public ActionResult Index() { //you can not access the below two mwthods using generic repository //var model = repository.GetEmployeesByDepartment("IT"); var model = employee_repository.GetEmployeesByGender("Male"); return View(model); } [HttpGet] public ActionResult AddEmployee() { return View(); } [HttpPost] public ActionResult AddEmployee(Employee model) { if (ModelState.IsValid) { repository.Insert(model); repository.Save(); return RedirectToAction("Index", "Employee"); } return View(); } [HttpGet] public ActionResult EditEmployee(int EmployeeId) { Employee model = repository.GetById(EmployeeId); return View(model); } [HttpPost] public ActionResult EditEmployee(Employee model) { if (ModelState.IsValid) { repository.Update(model); repository.Save(); return RedirectToAction("Index", "Employee"); } else { return View(model); } } [HttpGet] public ActionResult DeleteEmployee(int EmployeeId) { Employee model = repository.GetById(EmployeeId); return View(model); } [HttpPost] public ActionResult Delete(int EmployeeID) { repository.Delete(EmployeeID); repository.Save(); return RedirectToAction("Index", "Employee"); } } }
Note: Using a specific repository we can access all the operations. But using the generic repository we can access only the operations which are defined in the generic repository.
Now run the application and see everything is working as expected. In the next article, I am going to discuss how to use the Unit of Work using both generic and non-generic repositories in the ASP.NET MVC applications. Here, in this article, I try to explain how to implement both generic and non-generic repositories in ASP.NET MVC application using Entity Framework step by step with a simple example.
Nice Idea
Why not just have EmployeeRepository implement IGenericRepository? What benefit is there to creating IEmployeeRepository? It seems unlikely that any other class would implement it.
As mentioned if you read properly, IEmployeeRepository can add more operations that are specific to Employee while IGenericRepository defines common CRUD operations for all entities.