How to use Generic Repository Pattern in C#

By FoxLearn 2/16/2024 3:26:56 PM   235
This post shows you how to use Generic Repository Pattern in C# code

What is Generic Repository Pattern? How to use it in ASP.NET Core.

If you have a project and you realize that each repository performs similar or similar actions, if you create consecutive multiple similar repositories it will be a big waste. Instead, you only need to create one and only one repository for manipulation with all entity classes sufficient. And that is the Generic Repository Pattern.

Benefits of using Generic Repository Pattern

  • Minimize duplication of your code
  • Ensure the coder uses the same pattern
  • Easy for maintenance and testing
  • Minimize possible errors

Here I will make a simple example of a department. I will use Dependency Injection and Entity Framework Core to guide you.

public class BaseEntity
{
     public int Id { get; set; }
     public DateTime CreatedDate { get; set; }
     public DateTime ModifiedDate { get; set; }
}

Now we will create a "Department" class that simply consists of two attributes: departmental id and department name.

public class Department : BaseEntity
{
     public string DepartmentName { get; set; }
}

Now we will proceed to create an interface called IGenericRepository. This interface will have the methods Get, GetAll, Add, Update and Delete.

public interface IGenericRepository<T> where T : BaseEntity
{
     Task<T> Get(int? id);
     IEnumerable<T> GetAll();
     Task Add(T entity);
     Task Update(T entity);
     Task Delete(T entity);
}

Next, Create a GenericRepository class, then implement the IGenericRepository interface

public class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
    private readonly DbContext _context;
    private DbSet<T> _dbset;
    string errorMessage = string.Empty;
    public GenericRepository(ApplicationDbContext context)
    {
        _context = context;
        _dbset = context.Set<T>();
    }

    public async Task Add(T entity)
    {
        _dbset.Add(entity);
        await _context.SaveChangesAsync();
    }

    public async Task Delete(T entity)
    {
        _dbset.Remove(entity);
        await _context.SaveChangesAsync();
    }


    public async Task Update(T entity)
    {
        _dbset.Update(entity);
        await _context.SaveChangesAsync();

    }

    public async Task<T> Get(int? id)
    {
        return await _dbset.SingleOrDefaultAsync(s => s.Id == id);
    }

    public IEnumerable<T> GetAll()
    {
        return _dbset.AsEnumerable();
    }
}

You need to create a controller named DepartmentController as shown below.

public class DepartmentController : Controller
{
    private readonly IGenericRepository<Department> _context;

    public DepartmentController(IGenericRepository<Department> context)
    {
        _context = context;
    }

    // GET: Department
    public IActionResult Index()
    {
        return View(_context.GetAll());
    }

    // GET: Department/Details/5
    public async Task<IActionResult> Details(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        var department = await _context.Get(id);
        if (department == null)
        {
            return NotFound();
        }

        return View(department);
    }

    // GET: Department/Create
    public IActionResult Create()
    {
        return View();
    }

    // POST: Department/Create
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("Id,DepartmentName")] Department department)
    {
        if (ModelState.IsValid)
        {
            await _context.Add(department);
            return RedirectToAction("Index");
        }
        return View(department);
    }

    // GET: Department/Edit/5
    public async Task<IActionResult> Edit(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        var department = await _context.Get(id);
        if (department == null)
        {
            return NotFound();
        }
        return View(department);
    }

    // POST: Department/Edit/5
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Edit(int id, [Bind("Id,CreatedDate,ModifiedDate,DepartmentName")] Department department)
    {
        if (id != department.Id)
        {
            return NotFound();
        }

        if (ModelState.IsValid)
        {
            try
            {
                await _context.Update(department);
            }
            catch (DbUpdateConcurrencyException)
            {
            }
            return RedirectToAction("Index");
        }
        return View(department);
    }

    // GET: Department/Delete/5
    public async Task<IActionResult> Delete(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        var department = await _context.Get(id);
        await _context.Delete(department);
        if (department == null)
        {
            return NotFound();
        }

        return View(department);
    }

    // POST: Department/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> DeleteConfirmed(int id)
    {
        var department = await _context.Get(id);
        await _context.Delete(department);
        return RedirectToAction("Index");
    }
}

This is a simple example, my goal is to guide you to use the Generic Repository pattern, so you probably won't understand the details of how to use it.