الگوی طراحی 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();
}
}





همانطور که تا به اینجای کار می بینید کلاس 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 استفاده کنیم.


منبع:وبسایت پرووید