علاوه بر این ممکن است بخواهید برای برخی از entity ها عملیات خاصی را در Repository قرار بدهید. واضح است که کلاس Repository زمانی که به صورت جنریک پیادهسازی بشود شامل عملیات مشترکی است که تمامی entity ها از آنها استفاده خواهند کرد، اما اگر قرار باشد یک entity به طور خاص یک عملیات خاص را در خود داشته باشد باید Repository آن به صورت انحصاری پیادهسازی بگردد.
الگوی طراحی Repository به عنوان یک abstraction layer بین data access layer و business logic layer قرار میگیرد تا برنامه بتواند بدون درگیر شدن با جزئیات مربوط به دیتابیس عملیات CRUD را انجام بدهد. علاوه بر این، صحبت کردیم که الگوی طراحی Repository را میتوانیم به دو روش جنریک و غیرجنریک پیاده سازی کنیم. ضمناً، توصیه میکنیم حتماً قسمت های قبلی از این آموزش را مطالعه کنید تا بتوانید مطالب مربوط به این قسمت را به بهترین شکل ممکن درک کنید.
الگوی طراحی Repository زمانی که به صورت جنریک پیاده سازی می شود شامل تمامی عملیات مشترکی است که تمامی entity ها می خواهند از آنها استفاده کنند. این عملیات شامل Create و Retrieve و Update و Delete می باشد.
اما اگر قرار باشد که یک entity خاص، عملیات منحصر به فرد خود را در Repository داشته باشد، دیگر نمیتواند از کلاس Repository جنریک استفاده کند. در این شرایط این entity باید یک 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 استفاده کنیم.
حال می توانید برنامه را اجرا کنید و نحوه عملکرد آن را ببینید.
منبع: وبسایت پرووید