Bài viết dưới đây sẽ nói về Generic Repository Pattern, một mẫu lý tưởng giúp bạn tối ưu nhiều vấn đề trong đó có các đoạn mã của bạn và sau đó sẽ hướng dẫn các bạn sử dụng Generic Repository Pattern trong ASP.NET Core.
I. Generic Repository Pattern là gì ?
1. Định nghĩa Generic Repository Pattern
Nếu như bạn có một dự án và bạn nhận thấy mỗi repository đều thực hiện những hành động tương tự hoặc gần tương tự nhau, nếu bạn tạo liên tiếp nhiều repository gần giống nhau đó sẽ là một sự lãng phí lớn. Thay vào đó. bạn chỉ cần tạo một và chỉ một repository cho việc thao tác với toàn bộ các class entity là đủ. Và đó chính là Generic Repository Pattern.
2. Lợi ích khi sử dụng Generic Repositoty Pattern
- Giảm thiểu được sự trùng lập các đoạn code của bạn
- Đảm bảo các coder dùng chung một mẫu
- Dễ dàng cho việc bảo trì và kiểm thử
- Giảm thiểu các lỗi có thể xảy ra
II. Làm thế nào để sử dụng Generic Repository Pattern trong ASP.NET Core
1. Tạo model cần thiết
Tạo một class gọi là “BaseEntity” để tất cả các class khác có thể kế thừa các thuộc tính của nó:
1 2 3 4 5 6 |
public class BaseEntity { public Int64 Id { get; set; } public DateTime CreatedDate { get; set; } public DateTime ModifiedDate { get; set; } } |
Bây giờ ta tạo một class “PhongBan” chỉ đơn giản gồm 2 thuộc tính là mã phòng ban và tên phòng ban
1 2 3 4 5 |
public class PhongBan : BaseEntity { public string Ma { get; set; } public string Ten { get; set; } } |
2. Tạo Generic Repository
Bây giờ tiến hành tạo một interface có tên là IGenericRepository. Interface này sẽ có các phương thức Get, GetAll, Add, Update và Delete
1 2 3 4 5 6 7 8 |
public interface IGenericRepository<T> where T : BaseEntity { Task<T> Get(long? id); IEnumerable<T> GetAll(); Task Add(T entity); Task Update(T entity); Task Delete(T entity); } |
Và tiến hành hiện thực nó thôi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
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(long? id) { return await _dbset.SingleOrDefaultAsync(s => s.Id == id); } public IEnumerable<T> GetAll() { return _dbset.AsEnumerable(); } } |
3. Sử dụng Generic Repository
Bây giờ tiến hành tạo một controller có tên là PhongBansController có nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
public class PhongBansController : Controller { private readonly IGenericRepository<PhongBan> _context; public PhongBansController(IGenericRepository<PhongBan> context) { _context = context; } // GET: PhongBans public IActionResult Index() { return View(_context.GetAll()); } // GET: PhongBans/Details/5 public async Task<IActionResult> Details(long? id) { if (id == null) { return NotFound(); } var phongBan = await _context.Get(id); if (phongBan == null) { return NotFound(); } return View(phongBan); } // GET: PhongBans/Create public IActionResult Create() { return View(); } // POST: PhongBans/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,Ma,Ten")] PhongBan phongBan) { if (ModelState.IsValid) { await _context.Add(phongBan); return RedirectToAction("Index"); } return View(phongBan); } // GET: PhongBans/Edit/5 public async Task<IActionResult> Edit(long? id) { if (id == null) { return NotFound(); } var phongBan = await _context.Get(id); if (phongBan == null) { return NotFound(); } return View(phongBan); } // POST: PhongBans/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(long id, [Bind("Id,CreatedDate,Ma,ModifiedDate,Ten")] PhongBan phongBan) { if (id != phongBan.Id) { return NotFound(); } if (ModelState.IsValid) { try { await _context.Update(phongBan); } catch (DbUpdateConcurrencyException) { } return RedirectToAction("Index"); } return View(phongBan); } // GET: PhongBans/Delete/5 public async Task<IActionResult> Delete(long? id) { if (id == null) { return NotFound(); } var phongBan = await _context.Get(id); await _context.Delete(phongBan); if (phongBan == null) { return NotFound(); } return View(phongBan); } // POST: PhongBans/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task<IActionResult> DeleteConfirmed(long id) { var phongBan = await _context.Get(id); await _context.Delete(phongBan); return RedirectToAction("Index"); } } |
Các view của controller này các bạn tự tạo nhé.
III. Kết luận
Vì đây là ví dụ đơn giản, mục tiêu của mình là hướng dẫn các bạn cài đặt Generic Repository mà thôi, nên có thể bạn sẽ không hiểu hết được khả năng của nó, vì nếu dùng một ví dụ đầy đủ hơn sẽ rất là nhiều vì thế bạn hãy tự tìm hiểu thêm nhé.