Generic Repository Pattern in C#

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.

  1. What is Generic Repository Design Pattern in c#?
  2. Why do we need a Generic Repository Design Pattern?
  3. 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.

Why do we need Generic Repository Pattern in C#

Creating a Generic Repository:

A Generic Repository in C# typically does at least five operations as follows

  1. Selecting all records from a table
  2. Selecting a single record based on its primary key
  3. Insert a new record into a table
  4. Update an existing record in a table
  5. 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.

9 thoughts on “Generic Repository Pattern in C#”

  1. blank

    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 )

  2. blank

    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.

  3. blank

    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 ?

Leave a Reply

Your email address will not be published. Required fields are marked *