Mahedi Hasan Niloy
.Net Programming
Published in
7 min readSep 4, 2023

--

Photo by Emile Perron on Unsplash

Implement Generic Repository Pattern in C#

What is it: 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.

Why: Basic Repository or Non-Generic Repository, We need to create separate repositories for each and every entity present in our application. For example, if we have three entities such as Employee, Product, and Customer, then we need to create three repositories such as EmployeeRepository, ProductRepository, and CustomerRepository.

This is actually boring and repeating work, especially if all the repositories are going to 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

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.

Adding GenericRepository Folder.

Let us first add a folder in the root directory of the project 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 with the name IGenericRepository.cs and then copy and paste the following code into it. Here, you can see, instead of Employee, the interface is working with the T type where T is going to 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 that we created in the IEmployeeRepository interface of our previous article. Notice that instead of the Employee entity, now we are using 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. To do so, add a class file with the name GenericRepository within the GenericRepository Folder and then copy and paste the following code into it. 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, so 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 the reason why a generic DbSet variable is declared at the top that points to an appropriate DbSet based on the type of T. And 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, next 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 are going to be performed on the Employee table. 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;
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");
}
}
}

As shown in the above code, 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. Now run the application and perform the CRUD operations which should work as expected.

--

--

.Net Programming
.Net Programming

Published in .Net Programming

Explore the .Net with the latest in C# from basic to advanced, including .Net versions 9, 8, 6, 5, Core 3.1, .Net Framework, ASP.NET Core, MVC, design patterns, OOPS, and SOLID principles. Get top tutorials, best practices, and hands-on code examples on GitHub.

Mahedi Hasan Niloy
Mahedi Hasan Niloy

Written by Mahedi Hasan Niloy

Software Engineer | Mostly Write about .NET, C#. System Design Enthusiasts.