PDA

View Full Version : کد را در کجا تعریف کنم؟



SajjadKhati
دوشنبه 27 آذر 1402, 15:02 عصر
سلام دوستان .

یک سئوال در این رابطه داشتم که طبق در نظر گرفتن کدنویسی بصورت ماژولاریتی و اصول solid ، دقیق متوجه نشدم که سناریویی که در زیر میگم را در چه ماژولی تعریف کنم .


من دو موضوع و سناریو دارم .
قبل از همه ، موضوع درباره ی لایه ی دسترسی داده و ماژول با الگوی طراحی Repository هست .
مثلا موجودیت های زیر را (در زبان سی شارپ) داریم :




public class UserAccountEntity
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string FullName { get; set; }
public virtual ICollection<TruckEntity> TruckEntities { get; set; }
}



public class TruckEntity
{
public int Id { get; set; }
public string PlaqueNumber { get; set; }
}






=========================


موضوع اول :
در Repository هم متد جنریک Edit را برای به روزرسانی و ویرایش را نوشتم .
اما این متد Edit ، برای یک جدول خاص طراحی نشد . بنابراین نمیتونه Navigation Property یا Navigation Collection Property هایی (مثل پروپرتی TruckEntities در کد بالا) که به جدول دیگه مربوط هستند را پیدا کنه (مثلا اگر جدول UserAccountEntity را بخواهیم ویرایش کنیم ، نمیتونه رکوردهایی که از قبل که در TruckEntity بودند و مربوط به اون رکورد از UserAccountEntity بودند را پیدا کنه) و حذف کنه .


پس باید خودمان موقع به روزرسانی یک رکورد ، تمام رکوردهای مربوط به Navigation Property ها را پیدا و حذف کنیم و بعدش اون رکورد را به روزرسانی کنیم .


البته راهکار دیگه و متد آماده ای هم فکر کنم ef core داشته باشه اما فعلا این طور میخوام در نظر بگیرم که برای به روزرسانی ، ابتدا باید همه ی رکوردهای مربوط به جداول Navigation Property یا Navigation Collection Property ای که در اون جدول هستند را بصورت دستی حذف کنم و سپس جدول مورد نظر را ویرایش کنم .


حالا با در نظر گرفتن اصول solid و همچنین کدنویسی ماژولار ، این کدها را (کدهایی که قبل از فراخوانی متد Repository.Edit ، ابتدا تمام رکوردهای مربوط به Navigation Property یا Navigation Collection Property یی که مربوط به این رکورد از این جدول هستند را حذف میکنه و سپس متد Repository.Edit را فراخوانی میکنه) را کجا باید تعریف کرد؟


اگر این کدها را در ماژول Repository تعریف کنم ، خوب ماژولاریتیِ ماژولِ Repository را پایین میاره . یعنی انتقال ماژولِ Repository را به برنامه های دیگه ، کم میکنه . چون کدش شخصی سازی میشه و فقط برای استفاده در فقط همون موجودیت میتونه استفاده بشه (و نه حتی در موجودیت های دیگه هم استفاده نمیشه ، چه برسه به برنامه های دیگه) .


البته اگر از این روش برم (یعنی این کدها را در ماژول Repository تعریف کنم) ، ابتدا یک کلاس Repository ئه جنریک مینویسم که قابل انتقال به هر برنامه ای باشه و متدهاش را virtual میگیرم اما کلاس های فرزندش (که مثلا در اینجا میتونه TruckRepository و ... باشه) را فقط در همون برنامه استفاده میکنم که قابل انتقال نیست .
مثلا متد Edit را در کلاس فرزندش (مثلا در کلاس TruckRepository) هم میشه override کرد که قبل از فراخوانی متد پدر ، اون کدهای حذف کردن مربوط به جداول مربوط به Navigation Property یا Navigation Collection Property را انجام بده .




حالا اگر این کدها را در ماژول Repository ننویسم و بجایش ، درون ماژولی که از ماژول Repository داره استفاده میکنه ، تعریف کنم (یعنی درون ماژولی که از ماژول Repository و کلاس هاش شی میسازه ، تعریف کنم) ، در اون صورت خوبی اش اینه که ماژولاریتیِ ماژولِ Repository بالا میره و کل ماژول اش را میشه به هر برنامه ای منتقل کرد اما نمیدونم طبق اصل single responsibility در solid ، این کار درسته یا نه . چون کدهای حذف کردن (قبل از ویرایش) و کلا منطق دسترسی داده (Data Access Logic) ، مربوط به ماژول Repository و کلا مربوط به ماژول دسترسی داده هست که این کدهاش ، از این ماژول (ئه Repository) خارج شده .


در مقاله ی زیر در مایکروسافت ، چند جا اشاره کرد که وظیفه ی Repository ، منطق دسترسی داده هست :

Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application (9 of 10) | Microsoft Learn (https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application#implement-a-generic-repository-and-a-unit-of-work-class)



"You can implement a single repository for all entity types, or one for each type. If you implement one for each type, you can use separate classes, a generic base class and derived classes, or an abstract base class and derived classes.


You can include business logic in your repository or restrict it to data access logic."


======================


موضوع دوم :
مثلا فرض کنید که در یک موجودیت خاص ، میخواهیم طبق چند فیلد ، فیلتر کنیم .
مثلا در موجودیت UserAccountEntity ، میخواهیم طبق چند فیلد UserName و FullName فیلتر کنیم .
این هم معمولا بصورت خاص برای یک موجودیت خاص هست که کدش قابل انتقال به برنامه (یا موجودیت) دیگه نیست .


کد این را هم کجا تعریف کنیم؟ (درون ماژول Repository تعریف کنیم یا درون ماژولی که از Repository داره استفاده میکنه و ازش شی میسازه ، تعریف کنیم) ؟
کلا یک توضیحی میدین که از کجا متوجه بشم که این نوع کدها را در کجا تعریف کنیم؟


ضمنا مایکروسافت در همون مقاله ، دو کد زیر را نوشت :




public class StudentController : Controller
{
public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";


if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
ViewBag.CurrentFilter = searchString;


var students = from s in studentRepository.GetStudents()
select s;
if (!String.IsNullOrEmpty(searchString))
{
students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper ())
|| s.FirstMidName.ToUpper().Contains(searchString.ToU pper()));
}
switch (sortOrder)
{
case "name_desc":
students = students.OrderByDescending(s => s.LastName);
break;
case "Date":
students = students.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
students = students.OrderByDescending(s => s.EnrollmentDate);
break;
default: // Name ascending
students = students.OrderBy(s => s.LastName);
break;
}


int pageSize = 3;
int pageNumber = (page ?? 1);
return View(students.ToPagedList(pageNumber, pageSize));
}
}






و همچنین :






public class GenericRepository<TEntity> where TEntity : class
{
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;


if (filter != null)
{
query = query.Where(filter);
}


foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}


if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
}




که هر دو منطق دسترسی داده (Data Access Logic) هستند اما یکی را در ماژولی که از Repository استفاده میکرد ، تعریف کرد (در کلاس StudentController تعریف کرد) و یکی دیگه را در همون ماژول Repository تعریف کرد .
با اونکه خودش در اون متن در اون مقاله گفته بود که منطق دسترسی داده (و حتی بخشی از منطق تجاری) میتونه در Repository تعریف بشه .



لطفا دلیل تون را برای جواب هم بگید .


با تشکر ازتون .

ROSTAM2
پنج شنبه 30 آذر 1402, 04:52 صبح
سلام دوستان .

یک سئوال در این رابطه داشتم که طبق در نظر گرفتن کدنویسی بصورت ماژولاریتی و اصول solid ، دقیق متوجه نشدم که سناریویی که در زیر میگم را در چه ماژولی تعریف کنم .


من دو موضوع و سناریو دارم .
قبل از همه ، موضوع درباره ی لایه ی دسترسی داده و ماژول با الگوی طراحی Repository هست .
مثلا موجودیت های زیر را (در زبان سی شارپ) داریم :




public class UserAccountEntity
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string FullName { get; set; }
public virtual ICollection<TruckEntity> TruckEntities { get; set; }
}



public class TruckEntity
{
public int Id { get; set; }
public string PlaqueNumber { get; set; }
}






=========================


موضوع اول :
در Repository هم متد جنریک Edit را برای به روزرسانی و ویرایش را نوشتم .
اما این متد Edit ، برای یک جدول خاص طراحی نشد . بنابراین نمیتونه Navigation Property یا Navigation Collection Property هایی (مثل پروپرتی TruckEntities در کد بالا) که به جدول دیگه مربوط هستند را پیدا کنه (مثلا اگر جدول UserAccountEntity را بخواهیم ویرایش کنیم ، نمیتونه رکوردهایی که از قبل که در TruckEntity بودند و مربوط به اون رکورد از UserAccountEntity بودند را پیدا کنه) و حذف کنه .


پس باید خودمان موقع به روزرسانی یک رکورد ، تمام رکوردهای مربوط به Navigation Property ها را پیدا و حذف کنیم و بعدش اون رکورد را به روزرسانی کنیم .


البته راهکار دیگه و متد آماده ای هم فکر کنم ef core داشته باشه اما فعلا این طور میخوام در نظر بگیرم که برای به روزرسانی ، ابتدا باید همه ی رکوردهای مربوط به جداول Navigation Property یا Navigation Collection Property ای که در اون جدول هستند را بصورت دستی حذف کنم و سپس جدول مورد نظر را ویرایش کنم .


حالا با در نظر گرفتن اصول solid و همچنین کدنویسی ماژولار ، این کدها را (کدهایی که قبل از فراخوانی متد Repository.Edit ، ابتدا تمام رکوردهای مربوط به Navigation Property یا Navigation Collection Property یی که مربوط به این رکورد از این جدول هستند را حذف میکنه و سپس متد Repository.Edit را فراخوانی میکنه) را کجا باید تعریف کرد؟


اگر این کدها را در ماژول Repository تعریف کنم ، خوب ماژولاریتیِ ماژولِ Repository را پایین میاره . یعنی انتقال ماژولِ Repository را به برنامه های دیگه ، کم میکنه . چون کدش شخصی سازی میشه و فقط برای استفاده در فقط همون موجودیت میتونه استفاده بشه (و نه حتی در موجودیت های دیگه هم استفاده نمیشه ، چه برسه به برنامه های دیگه) .


البته اگر از این روش برم (یعنی این کدها را در ماژول Repository تعریف کنم) ، ابتدا یک کلاس Repository ئه جنریک مینویسم که قابل انتقال به هر برنامه ای باشه و متدهاش را virtual میگیرم اما کلاس های فرزندش (که مثلا در اینجا میتونه TruckRepository و ... باشه) را فقط در همون برنامه استفاده میکنم که قابل انتقال نیست .
مثلا متد Edit را در کلاس فرزندش (مثلا در کلاس TruckRepository) هم میشه override کرد که قبل از فراخوانی متد پدر ، اون کدهای حذف کردن مربوط به جداول مربوط به Navigation Property یا Navigation Collection Property را انجام بده .




حالا اگر این کدها را در ماژول Repository ننویسم و بجایش ، درون ماژولی که از ماژول Repository داره استفاده میکنه ، تعریف کنم (یعنی درون ماژولی که از ماژول Repository و کلاس هاش شی میسازه ، تعریف کنم) ، در اون صورت خوبی اش اینه که ماژولاریتیِ ماژولِ Repository بالا میره و کل ماژول اش را میشه به هر برنامه ای منتقل کرد اما نمیدونم طبق اصل single responsibility در solid ، این کار درسته یا نه . چون کدهای حذف کردن (قبل از ویرایش) و کلا منطق دسترسی داده (Data Access Logic) ، مربوط به ماژول Repository و کلا مربوط به ماژول دسترسی داده هست که این کدهاش ، از این ماژول (ئه Repository) خارج شده .


در مقاله ی زیر در مایکروسافت ، چند جا اشاره کرد که وظیفه ی Repository ، منطق دسترسی داده هست :

Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application (9 of 10) | Microsoft Learn (https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application#implement-a-generic-repository-and-a-unit-of-work-class)



"You can implement a single repository for all entity types, or one for each type. If you implement one for each type, you can use separate classes, a generic base class and derived classes, or an abstract base class and derived classes.


You can include business logic in your repository or restrict it to data access logic."


======================


موضوع دوم :
مثلا فرض کنید که در یک موجودیت خاص ، میخواهیم طبق چند فیلد ، فیلتر کنیم .
مثلا در موجودیت UserAccountEntity ، میخواهیم طبق چند فیلد UserName و FullName فیلتر کنیم .
این هم معمولا بصورت خاص برای یک موجودیت خاص هست که کدش قابل انتقال به برنامه (یا موجودیت) دیگه نیست .


کد این را هم کجا تعریف کنیم؟ (درون ماژول Repository تعریف کنیم یا درون ماژولی که از Repository داره استفاده میکنه و ازش شی میسازه ، تعریف کنیم) ؟
کلا یک توضیحی میدین که از کجا متوجه بشم که این نوع کدها را در کجا تعریف کنیم؟


ضمنا مایکروسافت در همون مقاله ، دو کد زیر را نوشت :




public class StudentController : Controller
{
public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";


if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
ViewBag.CurrentFilter = searchString;


var students = from s in studentRepository.GetStudents()
select s;
if (!String.IsNullOrEmpty(searchString))
{
students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper ())
|| s.FirstMidName.ToUpper().Contains(searchString.ToU pper()));
}
switch (sortOrder)
{
case "name_desc":
students = students.OrderByDescending(s => s.LastName);
break;
case "Date":
students = students.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
students = students.OrderByDescending(s => s.EnrollmentDate);
break;
default: // Name ascending
students = students.OrderBy(s => s.LastName);
break;
}


int pageSize = 3;
int pageNumber = (page ?? 1);
return View(students.ToPagedList(pageNumber, pageSize));
}
}






و همچنین :






public class GenericRepository<TEntity> where TEntity : class
{
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;


if (filter != null)
{
query = query.Where(filter);
}


foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}


if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
}




که هر دو منطق دسترسی داده (Data Access Logic) هستند اما یکی را در ماژولی که از Repository استفاده میکرد ، تعریف کرد (در کلاس StudentController تعریف کرد) و یکی دیگه را در همون ماژول Repository تعریف کرد .
با اونکه خودش در اون متن در اون مقاله گفته بود که منطق دسترسی داده (و حتی بخشی از منطق تجاری) میتونه در Repository تعریف بشه .



لطفا دلیل تون را برای جواب هم بگید .


با تشکر ازتون .

سلام
بنظر من زیادی شلوغش کردی.
برای هر کاری می خوای Query بنویس یا از LINQ استفاده کن. حالا تعریف کلاس دیگه اختیاریه....

ذخیره سازی فایل در دیتابیس - لیست پخش (aparat.com) (https://www.aparat.com/playlist/7967504)

System.Windows.Media.MediaPlayer - لیست پخش (aparat.com) (https://www.aparat.com/playlist/8014364)

SajjadKhati
پنج شنبه 30 آذر 1402, 14:22 عصر
سلام
بنظر من زیادی شلوغش کردی.
برای هر کاری می خوای Query بنویس یا از LINQ استفاده کن. حالا تعریف کلاس دیگه اختیاریه....

ذخیره سازی فایل در دیتابیس - لیست پخش (aparat.com) (https://www.aparat.com/playlist/7967504)

System.Windows.Media.MediaPlayer - لیست پخش (aparat.com) (https://www.aparat.com/playlist/8014364)

سلام
خیلی ممنون .
احتمالا منظورم را خوب متوجه نشدید .
اونها کدهای من نیستن . کدهای مایکروسافت هستن .

سئوال ام درباره ی solid و تعریف کد در چه مکانی بود .
تشکر