Back to: Design Patterns in C# With Real-Time Examples
Using Both Generic and Non-Generic Repository Patterns 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 C#. 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 entities, 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 (Non-Generic) Repositories for an entity.
As we already discussed the Repository Design Pattern in C# 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 pattern can be implemented in the following two ways. So, I strongly recommended you read the following two articles before proceeding to this article as we are also going to work with the same example that we created and worked on in our previous two articles.
Generic Repository Design Pattern in C#
The Generic Repository Design Pattern in C# is used to define common database operations such as Create, Retrieve, Update, Delete, etc. for all the database entities in a single class. That means the common operations for all the database entities are going to be defined inside the Generic Repository.
Non-Generic Repository Pattern (Specific Repository)
The Non-Generic Repository Design Pattern in C# is used to define the database operations which are related to a specific entity in 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. That means the common operations are going to be defined inside the Generic Repository, and the operations which are specific to an entity are going to be defined in a separate 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
With the Generic Repository, we cannot use specific operations for an entity, we can only define the common operations which are common for all entities of our application. In the case of a Non-Generic Repository, we have to create a separate repository for each entity which will have both common operations as well as specific operations of an entity. So a better way is, just to create a Generic Repository for commonly used CRUD operations, and for specific operations create a Non-Generic Repository for each entity and then inherit the Non-Generic Repository from the Generic Repository. For a better understanding, please have a look at the following image.
As you can see in the above image, the Generic Repository contains common operations such as GetAll, GetById, Insert, Update, and Delete which are going to be common for all Entities. The specific or Non-Generic Repository i.e. EmployeeRepository contains GetEmployeesByGender and GetEmployeesByDepartment operations which are going to be used only by Employee Entity, and again this Non-Generic Repository inherits from the Generic Repository. Similarly, Non-Generic Repository i.e. ProductRepository contains GetActiveProducts and GetProductsByCategory operations which are going to be used only by Product Entity, and again this ProductRepository inherits from the Generic Repository.
Example to Understand Generic and Non-Generic Repository in C#
We are going to work with the same example that we started in the Non-Generic Repository Design Pattern and Continue with Generic Repository Design Pattern articles. So please read these two articles before proceeding to this article. Let us first modify the IGenericRepository.cs file as shown below. This interface going to define the common database operations for all entities such as Employee, Customer, Product, Payment, etc. of our application.
using System.Collections.Generic; namespace RepositoryUsingEFinMVC.GenericRepository { //Here, we are creating the IGenericRepository interface as a Generic Interface //Here, we are applying the Generic Constraint //The constraint is T which is going to be a class 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(); } }
Modifying GenericRepository
Next, modify the GenericRepository.cs file as shown below. This class implements the IGenericRepository<T> interface where T is going to be a class. The following code is the implementation of the Generic Repository where we implement the code for common CRUD operation for each entity. The following code is self-explained, so please go through the comment lines for a better understanding.
using RepositoryUsingEFinMVC.DAL; using System.Collections.Generic; using System.Data.Entity; using System.Linq; namespace RepositoryUsingEFinMVC.GenericRepository { //The following GenericRepository class Implement the IGenericRepository Interface //And Here T is going to be a class //While Creating an Instance of the GenericRepository type, we need to specify the Class Name //That is we need to specify the actual class name of the type T public class GenericRepository<T> : IGenericRepository<T> where T : class { //The following variable is going to hold the EmployeeDBContext instance protected EmployeeDBContext _context = null; //The following Variable is going to hold the DbSet Entity protected DbSet<T> table = null; //Using the Parameterless Constructor, //we are initializing the context object and table variable public GenericRepository() { this._context = new EmployeeDBContext(); //Whatever class name we specify while creating the instance of GenericRepository //That class name will be stored in the table variable table = _context.Set<T>(); } //Using the Parameterized Constructor, //we are initializing the context object and table variable public GenericRepository(EmployeeDBContext _context) { this._context = _context; table = _context.Set<T>(); } //This method will return all the Records from the table public IEnumerable<T> GetAll() { return table.ToList(); } //This method will return the specified record from the table //based on the ID which it received as an argument public T GetById(object id) { return table.Find(id); } //This method will Insert one object into the table //It will receive the object as an argument which needs to be inserted into the database public void Insert(T obj) { //It will mark the Entity state as Added State table.Add(obj); } //This method is going to update the record in the table //It will receive the object as an argument public void Update(T obj) { //First attach the object to the table table.Attach(obj); //Then set the state of the Entity as Modified _context.Entry(obj).State = EntityState.Modified; } //This method is going to remove the record from the table //It will receive the primary key value as an argument whose information needs to be removed from the table public void Delete(object id) { //First, fetch the record from the table T existing = table.Find(id); //This will mark the Entity State as Deleted table.Remove(existing); } //This method will make the changes permanent in the database //That means once we call Insert, Update, and Delete Methods, //Then we need to call the Save method to make the changes permanent in the database public void Save() { _context.SaveChanges(); } } }
Creating Specific or Non-Generic Repositories:
Now we need to provide the Specific or Non-Generic implementation for each entity. Let’s say we want two extra operations for the Employee entity such as getting Employees By gender and getting employees by department. As these two operations are specific to the Employee entity there is no point to add these two operations to the Generic Repository.
So we need to create a Non-Generic or Specific Repository for the Employee Entity let us say EmployeeRepository which will contain the two specific operations, as well as this Non-Generic Repository, which is also inherited from GenericRepository. Within this repository, we need to provide the two specific operations as shown below. So, 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> { //Here, you need to define the operations which are specific to Employee Entity IEnumerable<Employee> GetEmployeesByGender(string Gender); IEnumerable<Employee> GetEmployeesByDepartment(string Dept); } }
Modifying EmployeeRepository.cs file
Next, modify the EmployeeRepository.cs file as shown below. Here, you can see, it is providing implementations for the IEmployeeRepository methods. Further, this class is inherited from the GenericRepository<Employee> class. Here, we are specifying the Generic Type as Employee. Here, we are using the Parent class i.e. GenericRepository class _context object to access the Employee entity.
using RepositoryUsingEFinMVC.DAL; using System.Collections.Generic; using System.Linq; using RepositoryUsingEFinMVC.GenericRepository; 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(); } } }
Modifying Employee Controller.
Now we need to use both Generic and Non-Generic repositories inside the Employee Controller. So, modify the Employee Controller as shown below. The following code is self-explained, so please go through the comment lines for a better understanding.
using System.Web.Mvc; using RepositoryUsingEFinMVC.DAL; using RepositoryUsingEFinMVC.GenericRepository; using RepositoryUsingEFinMVC.Repository; namespace RepositoryUsingEFinMVC.Controllers { public class EmployeeController : Controller { private IGenericRepository<Employee> genericRepository = null; private IEmployeeRepository employeeRepository = null; public EmployeeController() { this.employeeRepository = new EmployeeRepository(); this.genericRepository = new GenericRepository<Employee>(); } public EmployeeController(EmployeeRepository repository) { this.employeeRepository = repository; } public EmployeeController(IGenericRepository<Employee> repository) { this.genericRepository = repository; } //The following Action Method is used to return all the Employees [HttpGet] public ActionResult Index() { //you cannot access the GetEmployeesByDepartment and GetEmployeesByGender methods using generic repository //var model = repository.GetEmployeesByDepartment("IT"); var model = employeeRepository.GetEmployeesByGender("Male"); return View(model); } //The following Action Method will open the Add Employee view [HttpGet] public ActionResult AddEmployee() { return View(); } //The following Action Method will be called when you click on the Submit button on the Add Employee view [HttpPost] public ActionResult AddEmployee(Employee model) { //First Check whether the Model State is Valid or not if (ModelState.IsValid) { //If Model State is Valid, then call the Insert method GenericRepository to make the Entity State Added genericRepository.Insert(model); //To make the changes permanent in the database, call the Save method of GenericRepository genericRepository.Save(); //Once the data is saved into the database, redirect to the Index View return RedirectToAction("Index", "Employee"); } //If the Model state is not valid, then stay on the current AddEmployee view return View(); } //The following Action Method will open the Edit Employee view based on the EmployeeId [HttpGet] public ActionResult EditEmployee(int EmployeeId) { //First, Fetch the Employee information by calling the GetById method of GenericRepository Employee model = genericRepository.GetById(EmployeeId); //Then Pass the Employee data to the Edit view return View(model); } //The following Action Method will be called when you click on the Submit button on the Edit Employee view [HttpPost] public ActionResult EditEmployee(Employee model) { //First Check whether the Model State is Valid or not if (ModelState.IsValid) { //If Valid, then call the Update Method of GenericRepository to make the Entity State Modified genericRepository.Update(model); //To make the changes permanent in the database, call the Save method of GenericRepository genericRepository.Save(); //Once the updated data is saved into the database, redirect to the Index View return RedirectToAction("Index", "Employee"); } else { //If the Model State is invalid, then stay on the same view return View(model); } } //The following Action Method will open the Delete Employee view based on the EmployeeId [HttpGet] public ActionResult DeleteEmployee(int EmployeeId) { //First, Fetch the Employee information by calling the GetById method of GenericRepository Employee model = genericRepository.GetById(EmployeeId); //Then Pass the Employee data to the Delete view return View(model); } //The following Action Method will be called when you click on the Submit button on the Delete Employee view [HttpPost] public ActionResult Delete(int EmployeeID) { //Call the Delete Method of the GenericRepository to Make the Entity State Deleted genericRepository.Delete(EmployeeID); //Then Call the Save Method to delete the entity from the database permanently genericRepository.Save(); //And finally, redirect the user to the Index View 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 if 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 Applications with Entity Framework.
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.
Nice Article,
Thank you so much,
I just have little confusion, we can also use the all methods of generic repo from specific repo, because of specific repo inherit the generic repo..
Thanks