Back to: Design Patterns in C# With Real-Time Examples
Generic Repository Pattern in C# with Examples
In this article, I will discuss the Generic Repository Design Pattern in C# with Examples. Please read our previous article, where we discussed the Basic or Non-Generic Repository Pattern in C# with an example, as we will work with the same example, which uses ASP.NET MVC and Entity Framework. You will understand the following pointers in detail at the end of this article.
- What is Generic Repository Design Pattern in c#?
- Why do we need a Generic Repository Design Pattern?
- How to implement Generic Repository Design Pattern in C#?
Note: We will implement the Generic Repository Design Pattern using the ASP.NET MVC application with Entity Framework using C# Language. In fact, we will work with the same example we worked on in our previous article. So, I strongly recommend you read our Previous Article before proceeding with this article.
What is Generic Repository Design Pattern in C#?
The Generic Repository pattern in C# is a design pattern that abstracts the application’s data layer, making it easier to manage data access logic across different data sources. It aims to reduce redundancy by implementing common data operations in a single, generic repository rather than having separate repositories for each entity type.
Key Concepts
- Generic Interface: A generic repository typically starts with a generic interface defining common operations like Add, Delete, Find, and Update. These operations are defined in a generic way, applicable to any entity type.
- Implementation: The generic interface is then implemented in a concrete class. This class handles the data source interactions, such as querying a database using an ORM (like Entity Framework).
- Entity Framework Context: The implementation will often utilize an Entity Framework context to interact with the database.
Why do we need a Generic Repository Design Pattern in C#?
As we already discussed, in a Basic Repository or Non-Generic Repository, we need to create separate repositories for every entity in our application. For example, if we have three entities, Employee, Product, and Customer, we need to create three repositories: EmployeeRepository, ProductRepository, and CustomerRepository.
This is boring and repetitive work, especially if all the repositories will do the same kind of work (i.e., typically database CRUD operations). This is against the DRY (Don’t Repeat Yourself) principle, as we are repeating the same code again and again in each repository. To solve the above problem, the Generic Repository Design Pattern comes into the picture. Please have a look at the below diagram for a better understanding.
Creating a Generic Repository:
A Generic Repository in C# typically does at least five operations as follows
- Selecting all records from a table
- Selecting a single record based on its primary key
- Insert a new record into a table
- Update an existing record in a table
- Delete an existing record from a table
However, the above list is not fixed. You may have more or fewer methods in your generic repository as per your business requirements. For the simplicity of this demo, let’s assume our Generic Repository will perform the above Five Operations. To implement a Generic Repository in C#, first, we need to create an interface, let’s say IGenericRepository, with the above five methods, and then we need to create a class, let’s say GenericRepository which will implement the IGenericRepository interface, and provide the generic implementations for the IGenericRepository interface methods. Let us proceed and implement this step by step in our existing ASP.NET MVC Application.
Adding GenericRepository Folder.
First, Let us add a folder in the project’s root directory with the name GenericRepository. To do so, right-click on the Project => Add => New Folder and then Rename the folder name as GenericRepository.
Creating Generic Repository Interface:
Next, add an Interface within the GenericRepository folder named IGenericRepository.cs and copy and paste the following code. Here, you can see that instead of Employee, the interface works with the T type, where T will be a class. That T can be Employee, Product, Customer, etc.Â
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 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(); } }
The IGenericRepository interface is a generic interface that defines the same set of five methods we created in the IEmployeeRepository interface of our previous article. Notice that instead of the Employee entity, we use T everywhere. Also, notice that GetById() and Delete() methods now accept object parameters instead of integer parameters. This is necessary because different tables may have different types of primary keys (The Customers table has a string primary key, whereas the Employees table has an integer primary key).
Implementing IGenericRepository Interface
Now, we need to implement the IGenericRepository interface. Add a class file named GenericRepository within the GenericRepository Folder and copy and paste the following code. The following GenericRepository<T> class is a generic class and implements the IGenericRepository<T> interface. As the following GenericRepository class uses the generic type, T, we can’t access a DbSet as a property of the data context object. Because we don’t know in advance what DbSet type we need to use, i.e., it may be Employee, Product, Customer, etc. That is why a generic DbSet variable is declared at the top that points to an appropriate DbSet based on the type of T. Then, using that DbSet variable, we are doing the operations.Â
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 private EmployeeDBContext _context = null; //The following Variable is going to hold the DbSet Entity private 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(); } } }
Modifying Employee Controller
Once the GenericRepository is ready, we need to use that generic repository in our Employee Controller. So, modify the Employee Controller as shown below. The following Controller uses the GenericRepository to perform the CRUD Operations. Further, if you notice, while creating the instance of GenericRepository, we have specified the type T as Employee. So, in this case, DbSet<T> will be replaced as DbSet<Employee> in the GenericRepository, and the operations will be performed on the Employee table.Â
using System.Web.Mvc; using RepositoryUsingEFinMVC.DAL; using RepositoryUsingEFinMVC.GenericRepository; namespace RepositoryUsingEFinMVC.Controllers { public class EmployeeController : Controller { //Create a variable to hold the instance of GenericRepository private IGenericRepository<Employee> genericRepository = null; //Initializing the genericRepository through a parameterless constructor public EmployeeController() { this.genericRepository = new GenericRepository<Employee>(); } //If you want to Initialize genericRepository using Dependency Injection Container, //Then include the following Parameterized Constructor //public EmployeeController(IGenericRepository<Employee> repository) //{ // this.genericRepository = repository; //} //The following Action Method is used to return all the Employees [HttpGet] public ActionResult Index() { //Call the GetAll Method of the GenericRepository instance var model = genericRepository.GetAll(); 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"); } } }
The above code shows that the IGenericRepository variable is declared with Employee as its type. The constructor then assigns an instance of GenericRepository or some other implementation of IGenericRepository to this variable.
That’s it. We are done with our implementation. Run the application and perform the CRUD operations, which should work as expected. This article gave you an understanding of the Generic Repository Pattern in C# from the ASP.NET MVC and Entity Framework context.Â
When to Use Generic Repository Pattern in C#?
Here are some situations where using the Generic Repository Pattern is beneficial:
- CRUD Operations: If your application performs standard Create, Read, Update, and Delete (CRUD) operations and these operations are largely the same across different entities or data models, a generic repository can reduce repetitive code.
- Decoupling: When you want to decouple the business logic of your application from the data access layer, a generic repository can act as a middle layer, making it easier to modify or replace your data access logic without impacting the rest of your application.
- Testing and Mocking: In unit testing, mock data access logic is often necessary. A generic repository can simplify this process because you only need to mock a single, generic repository rather than individual repositories for each entity.
- Code Reusability: If your application handles multiple entities that share similar data access patterns, using a generic repository can promote code reusability and reduce the need for redundant code.
- Maintainability: A generic repository can make the codebase cleaner and more maintainable, especially in large applications where managing individual repositories for each entity can become cumbersome.
- Simple Data Models: For applications with simple data models and straightforward data access requirements, a generic repository provides a quick and easy way to implement data access without overcomplicating the architecture.
However, it’s also important to recognize scenarios where the Generic Repository Pattern might not be the best choice:
- Complex Queries: If your application requires complex queries, the generic repository can become a limitation, as it might not be able to handle specific query needs efficiently.
- Over-Abstraction: Sometimes, the abstraction provided by the generic repository can hide important details of the data access layer, making it harder to optimize or handle specific data access scenarios.
- Performance Concerns: A generic repository might introduce unnecessary overhead for performance-critical applications, especially if the pattern leads to inefficient database access.
We created Entity-Specific Repositories in our previous article and Generic Repository in this article. In the next article, I will discuss How to use both Generic and Non-Generic Repositories in ASP.NET MVC Applications. In this article, I explain the Generic Repository Design Pattern in C# using ASP.NET MVC application with Entity Framework. I hope you enjoy this Generic Repository Design Pattern in C# with Examples article.
what if i want to add a method to a specific Model ? (i think i must create an interface and repository class for that model and write my method inside it )
Please read our next article where we discussed how to implement both Generic and specific repository in an application.
https://dotnettutorials.net/lesson/repository-pattern-implementation-guidelines-csharp/
This is suboptimal. Remember that a controller is instantiated for every request by the ControllerFactory (which by default is the DefaultControllerFactory). Each time an HTTP request is received, your implementation is creating a new database context.
Instead, one should instantiate one context in the controller and pass it as an argument to the GenericRepository.
Excellent article and very helpful
Thank you for sharing it.
Please post the db context file and also suggest how to map entity types dynamically with the dbcontext.
Please check our previous article as this is a continuation part to our previous article. In our previous article we discussed about Entity Framework.
How to inject this unityofwork and genericRepository in startup class for .net core project
Thnaks very much indeed!
What happens if I don’t give a particular data in update. Like for example in personal details table, I’ll get the mail id from user and I’ll save it in first. Then later I’ll get the other personal details input from the user “This time I’ll not pass the email”. Does the value for email will be null now ?