Mẫu thiết kế repository được sử dụng để tạo ra một lớp abstract giữa lớp DAL và lớp BL để thực hiện các thao tác CRUD (Create, Read, Update, Delete) đối với cơ sở dữ liệu bên dưới. Chúng ta cũng đã thảo luận rằng mẫu thiết kế repository có thể được thực hiện theo hai cách sau đây.
Generic Repository Pattern
Mẫu generic Repository được sử dụng để định nghĩa các thao tác như CRUD cho tất cả các thực thể CSDL trong một lớp duy nhất.
Non-Generic Repository Pattern (Specific Repository)
Mẫu Non-Generric Repository được sử dụng để định nghĩa tất cả các thao tác cơ sở dữ liệu liên quan đến một thực thể cụ thể trong một lớp riêng biệt. Ví dụ, nếu bạn có hai thực thể là Nhân viên (Employee) và Khách hàng (Customer), thì mỗi thực thể sẽ có lớp repository đặc thù riêng của nó để thực hiện các thao tác cơ sở dữ liệu đối với thực thể đó. Trước khi triển khai cả các Generic Repository và Non-Generic Repository, chúng ta cần phải hiểu rõ cách triển khai. Điều này có nghĩa là khi nào thì nên sử dụng generic repository, khi nào thì nên sử dụng non-generic repository và khi nào thì nên sử dụng cả generic repository và non-generic repository trong cùng một ứng dụng. Trong bài viết này, mình sẽ thảo luận về Mẫu Thiết kế Repository trong C# với một ví dụ sử dụng Entity Framework Core và ứng dụng ASP.NET MVC. Mẫu Thiết kế Repository trong C# là một trong những mẫu thiết kế được sử dụng nhiều nhất trong ứng dụng thời gian thực. Tại cuối bài viết này, bạn sẽ hiểu những điểm chính của Generic Repository.
Cách triển khai mẫu Repository
Nếu bạn sử dụng một trong các triển khai trên, thì với cách triển khai generic repository, bạn không thể sử dụng các thao tác cụ thể cho một thực thể và trong trường hợp triển khai non-generic repository, bạn phải viết code cho các thao tác CRUD chung cho mỗi thực thể, nghĩa là với mỗi mỗi 1 class Customer, Employee bạn phải khai báo thao tác CRUD cho nó những 2 lần và với cách triển khai generic repository thì bạn chỉ cần khai báo thao tác CRUD 1 lần duy nhất. Vì vậy, cách tốt hơn là chỉ tạo một generic repository cho các thao tác CRUD thường được sử dụng và cho thao tác cụ thể, tạo một non-generic và kế thừa từ generic repository. Sơ đồ dưới đây giải thích những điều trên.
Hiểu rõ hơn về Generic Repository, Non-Generic Repository thông qua một ví dụ cụ thể.
Sửa đổi tập tin IGenericRepository.cs như được hiển thị bên dưới.
namespace RepositoryUsingEFinMVC.GenericRepository
{ 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(); }
}
Thay đổi file GenericRepository.cs như bên dưới
namespace RepositoryUsingEFinMVC.GenericRepository
{ public class GenericRepository<T> : IGenericRepository<T> where T : class { public EmployeeDBContext _context = null; public DbSet<T> table = null; public GenericRepository() { this._context = new EmployeeDBContext(); table = _context.Set<T>(); } public GenericRepository(EmployeeDBContext _context) { this._context = _context; table = _context.Set<T>(); } public IEnumerable<T> GetAll() { return table.ToList(); } public T GetById(object id) { return table.Find(id); } public void Insert(T obj) { table.Add(obj); } public void Update(T obj) { table.Attach(obj); _context.Entry(obj).State = EntityState.Modified; } public void Delete(object id) { T existing = table.Find(id); table.Remove(existing); } public void Save() { _context.SaveChanges(); } }
}
Lưu ý: Đoạn mã trên là khai báo của Generic Repository, trong đó chúng ta khai báo thao tác chung CRUD chung cho mỗi thực thể.
Bây giờ chúng ta cần khai báo cụ thể cho mỗi thực thể. Hãy giả sử chúng ta muốn thêm hai thao tác bổ sung cho thực thể Employee như lấy Danh sách Nhân viên theo giới tính và lấy Danh sách Nhân viên theo bộ phận. Vì hai thao tác này thêm cho thực thể Employee nên không có nghĩa là khai báo 2 thao tác này vào Generic Repository
Vì vậy, chúng ta cần tạo một Repository không phải là generic có tên là EmployeeRepository, nó sẽ kế thừa từ GenericRepository. Trong Repository này, chúng ta cần cung cấp hai thao tác cụ thể như được hiển thị bên dưới.
Thay đổi file IEmployeeRepository.cs như bên dưới
using RepositoryUsingEFinMVC.DAL;
using RepositoryUsingEFinMVC.GenericRepository;
using System.Collections.Generic;
namespace RepositoryUsingEFinMVC.Repository
{ public interface IEmployeeRepository : IGenericRepository<Employee> { IEnumerable<Employee> GetEmployeesByGender(string Gender); IEnumerable<Employee> GetEmployeesByDepartment(string Dept); }
}
Thay đổi file EmployeeRepository.cs như bên dưới
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(); } } }
Bây giờ chúng ta cần sử dụng cả Generic Repository và Non-Generic Repository trong Employee Controller.
Thay đổi file Employee Controller như bên dưới
using RepositoryUsingEFinMVC.Repository;
using System.Web.Mvc;
using RepositoryUsingEFinMVC.DAL;
using RepositoryUsingEFinMVC.GenericRepository;
namespace RepositoryUsingEFinMVC.Controllers
{ public class EmployeeController : Controller { private IGenericRepository<Employee> repository = null; private IEmployeeRepository employee_repository = null; public EmployeeController() { this.employee_repository = new EmployeeRepository(); this.repository = new GenericRepository<Employee>(); } public EmployeeController(EmployeeRepository repository) { this.employee_repository = repository; } public EmployeeController(IGenericRepository<Employee> repository) { this.repository = repository; } [HttpGet] public ActionResult Index() { //you can not access the below two mwthods using generic repository //var model = repository.GetEmployeesByDepartment("IT"); var model = employee_repository.GetEmployeesByGender("Male"); return View(model); } [HttpGet] public ActionResult AddEmployee() { return View(); } [HttpPost] public ActionResult AddEmployee(Employee model) { if (ModelState.IsValid) { repository.Insert(model); repository.Save(); return RedirectToAction("Index", "Employee"); } return View(); } [HttpGet] public ActionResult EditEmployee(int EmployeeId) { Employee model = repository.GetById(EmployeeId); return View(model); } [HttpPost] public ActionResult EditEmployee(Employee model) { if (ModelState.IsValid) { repository.Update(model); repository.Save(); return RedirectToAction("Index", "Employee"); } else { return View(model); } } [HttpGet] public ActionResult DeleteEmployee(int EmployeeId) { Employee model = repository.GetById(EmployeeId); return View(model); } [HttpPost] public ActionResult Delete(int EmployeeID) { repository.Delete(EmployeeID); repository.Save(); return RedirectToAction("Index", "Employee"); } }
}
Chú ý: Bằng cách sử dụng một Non-Generic Repository, chúng ta có thể truy cập tất cả các thao tác. Nhưng bằng cách sử dụng Generic-Repository, chúng ta chỉ có thể truy cập các thao tác được định nghĩa trong kho chứa thông thường.
**Nguồn: **https://dotnettutorials.net/lesson/repository-pattern-implementation-guidelines-csharp/