استفاده کردن همزمان از کلاس Repository جنریک و غیر جنریک

علاوه بر این ممکن است بخواهید برای برخی از entity ها عملیات خاصی را در Repository قرار بدهید. واضح است که کلاس Repository زمانی که به صورت جنریک پیاده‌سازی بشود شامل عملیات مشترکی است که تمامی entity ها از آنها استفاده خواهند کرد، اما اگر قرار باشد یک entity به طور خاص یک عملیات خاص را در خود داشته باشد باید Repository آن به صورت انحصاری پیاده‌سازی بگردد.الگوی طراحی Repository به عنوان یک abstraction layer بین data access layer و business logic layer قرار می‌گیرد تا برنامه بتواند بدون درگیر شدن با جزئیات مربوط به دیتابیس عملیات CRUD را انجام بدهد. علاوه بر این، صحبت کردیم که الگوی طراحی Repository را می‌توانیم به دو روش جنریک و غیرجنریک پیاده سازی کنیم. ضمناً، توصیه می‌کنیم حتماً قسمت های قبلی از این آموزش را مطالعه کنید تا بتوانید مطالب مربوط به این قسمت را به بهترین شکل ممکن درک کنید.الگوی طراحی Repository به صورت جنریکالگوی طراحی Repository زمانی که به صورت جنریک پیاده سازی می شود شامل تمامی عملیات مشترکی است که تمامی entity ها می خواهند از آنها استفاده کنند. این عملیات شامل Create و Retrieve و Update و Delete می باشد.پیاده سازی الگوی Repository به صورت غیرجنریکاما اگر قرار باشد که یک entity خاص، عملیات منحصر به فرد خود را در Repository داشته باشد، دیگر نمی‌تواند از کلاس Repository جنریک استفاده کند. در این شرایط این entity باید یک Repository منحصر به فرد را برای خود تعریف کند و عملیاتی که قصد دارد از آنها استفاده کند را در آن قرار بدهد.اما قبل از اینکه در رابطه با این موضوع صحبت کنیم بگذارید یک راهنمایی ساده در رابطه با زمان استفاده صحیح از کلاس Repository به صورت جنریک و غیرجنریک خدمت شما عرض کنیم.راهنمای پیاده سازی الگوی طراحی Repositoryهمانطور که تا به اینجای کار مطلع هستید زمانی که از یک کلاس Repository به صورت جنریک استفاده می کنید باید تمامی متدها مشترکاً توسط تمامی entity ها استفاده بشود. این در حالی است که در زمان استفاده از کلاس Repository به صورت غیر جنریک می‌بایست کدهای تکراری زیادی را در برنامه قرار بدهید. پس بهترین روش این است که ابتدا یک کلاس Repository را به صورت جنریک تعریف کرده و تمامی عملیات CRUD مشترک را در آن قرار دهید و سپس برای هر کدام از entity ها که نیاز به عملیات خاص دارند یک کلاس Repository غیرجنریک و منحصر به فرد را ایجاد کنید که از آن کلاس Repository جنریک ارث بری کند. این موضوع وراثت می تواند از نوشتن کدهای تکراری شدیداً جلوگیری کند. تصویر زیر نیز دقیقاً همین موضوع را نشان می دهد.بررسی یک مثال کاربردیبرای درک هرچه بهتر این موضوع و ارث بری کردن یک کلاس Repository غیرجنریک که از یک کلاس Repository جنریک بهتر است که مثالی را بررسی کنیم. البته قبل از این توصیه می‌کنیم حتماً مقاله های قبل از این سری آموزشی را مطالعه کنید.در ابتدا تغییرات کوچکی در اینترفیس IGenericRepository ایجاد خواهیم کرد. این موضوع در کد زیر نشان داده شده است.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();}}پس از آن کلاس GenericRepository را شبیه به کدی که در قسمت زیر می بینید تغییر بدهیم.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();}}}همانطور که تا به اینجای کار می بینید کلاس Repository جنریک عملیات مشترک مربوط به تمامی entity ها را در خود جای داده است. حال فرض کنید که برای کلاس Employee قرار است عملیات خاصی را در Repository را لحاظ کنیم. برای مثال می خواهیم بتوانیم Employee ها را بر اساس جنسیت آنها از بانک اطلاعاتی استخراج کنیم و یا حتی آنها را بر اساس دپارتمانی که در آن کار می‌کنند بازیابی کنید.به منظور پیاده‌سازی کردن این دو عملیات منحصر به فرد برای این entity می‌بایستی یک کلاس غیر جنریک یک Repository غیرجنریک ایجاد کنیم. این کلاس غیرجنریک را EmployeeRepository نام می گذاریم که از کلاس GenericRepository ارث بری خواهد کرد و سپس دو عملیات منحصر به فرد برای این entity را به آن اضافه می‌کنیم. البته نباید از تعریف کردن یک interface برای این Repository غفلت کرد؛ چرا که این موضوع می‌تواند به قابلیت تست پذیری و نگهداری برنامه کمک کند. ضمناً، در رابطه با اهمیت نوشتن تست در برنامه‌هایی که با ASP.NET MVC پیاده سازی می شود کدی که در قسمت زیر مشاهده می کنید اینترفیس IEmployeeRepository را نشان می‌دهد.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);}}ضمناً کدی که در قسمت زیر مشاهده می کنید کلاس EmployeeRepository که از اینترفیس IEmployeeRepository و کلاس GenericRepository استفاده می کند را نشان می دهد.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();}}}حال می بایست که در Controller برنامه هم از کلاس Repository جنریک و هم از کلاس Repository غیرجنریک استفاده کنید. این موضوع در کد زیر نشان داده شده است.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");}}}با انجام این کار و با استفاده از کلاس Repository جنریک می‌توانیم از تمامی عملیات مشترکی که در اینترفیس مورد نظر تعریف شده است استفاده کنیم و زمانی که می خواهیم از عملیات خاص مربوط به Employee استفاده کنیم می بایست از کلاس غیر جنریک Repository استفاده کنیم.حال می توانید برنامه را اجرا کنید و نحوه عملکرد آن را ببینید. منبع: وبسایت پرووید