حل مشكل چرخه حذف در DOTNET Core:
زمانی كه مدلها و روابط بین آنها مشخص می شود رابطه های یك به چند اگر با دقت مدیریت نشوند ممكن است خطرناك باشند، به صورت پیشفرض عمل حذف و به روز رسانی به صورت كسكید انجام می شود یعنی اگر یك والد را حذف كنید خودكار تمام فرزندهای آن نیز حذف می شوند، امری طبیعی است ولی مدیری را در نظر بگیرید كه به اشتباه یك دسته از دسته بندی های محصولات را حذف می كند!
در این گونه موارد برنامه نویس یا باید عمل كسكید را غیرفعال كنید و یا قبل از انجام عملیات حذف بررسی شود ركورد مورد نظر اگر دارای فرزند بود به كاربر هشدار داده شود و در صورت تایید حذف شود.
حالا میخواهیم حالت بدتر این موضوع را بررسی كنیم:
ممكن است اتفاق بیفتد كه سه جدول داریم به صورتی كه جدول اول با جدول دوم، جدول دوم با جدول سوم و جدول سوم با جدول اول رابطه یك به چند دارد.
حالا اگر حالت كسكسید فعال باشد اگر از یكی از جداول ركوردی حذف شود عمل حذف به صورت چرخه تمام اطلاعات هر سه جدول را حذف خواهد كرد!
وقتی روابط به این صورت باشند دات نت زمان به روز رسانی بانك اطلاعاتی (Migration) پیامی مبنی بر اینكه این روابط چرخه حذف بوجود می آورند بدهد و از به روزرسانی بانك جلوگیری كند، در حالی كه به این روابط نیاز داریم و در حین برنامه نویسی كنترل خواهیم كرد كه این چرخه هیچ وقت اتفاق نیفتد و قبل از حذف ركورد تعداد فرزندان اگر بیشتر از صفر بود ركورد حذف نشود.
ولی بازهم اجازه به روزرسانی داده نمی شود بخاطر اینكه ساختار روابط همچنان میگوید چرخه حذف داریم!
برای این كار باید در DbContext تنظیمات زیر را اعمال كنیم تا بفهمانیم عمل حذف به صورت كسكید انجام نشود و مدیریت آن به برنامه نویس داده شود(به زبان ساده تر خطرات چرخه حذف را خودم مدیریت می كنم).
میتوانیم این تنظیم را برای هر مدل انجام دهیم اما معمولا تعداد مدلها در برنامه زیاد هستند و اعمال تنظیمات برای هر مدل به صورت جدا هم زمان و هم كد بیشتری را میخواهد، قطعه كد زیر این تنظیمات را برای تمام روابط اعمال خواهد كرد:
protected override void OnModelCreating(ModelBuilder modelbuilder)
{
foreach (var relationship in modelbuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
base.OnModelCreating(modelbuilder);
}
@WebDevelopmentReferences
#ASP_NET_CORE #EntityFramework #ASPNet
زمانی كه مدلها و روابط بین آنها مشخص می شود رابطه های یك به چند اگر با دقت مدیریت نشوند ممكن است خطرناك باشند، به صورت پیشفرض عمل حذف و به روز رسانی به صورت كسكید انجام می شود یعنی اگر یك والد را حذف كنید خودكار تمام فرزندهای آن نیز حذف می شوند، امری طبیعی است ولی مدیری را در نظر بگیرید كه به اشتباه یك دسته از دسته بندی های محصولات را حذف می كند!
در این گونه موارد برنامه نویس یا باید عمل كسكید را غیرفعال كنید و یا قبل از انجام عملیات حذف بررسی شود ركورد مورد نظر اگر دارای فرزند بود به كاربر هشدار داده شود و در صورت تایید حذف شود.
حالا میخواهیم حالت بدتر این موضوع را بررسی كنیم:
ممكن است اتفاق بیفتد كه سه جدول داریم به صورتی كه جدول اول با جدول دوم، جدول دوم با جدول سوم و جدول سوم با جدول اول رابطه یك به چند دارد.
حالا اگر حالت كسكسید فعال باشد اگر از یكی از جداول ركوردی حذف شود عمل حذف به صورت چرخه تمام اطلاعات هر سه جدول را حذف خواهد كرد!
وقتی روابط به این صورت باشند دات نت زمان به روز رسانی بانك اطلاعاتی (Migration) پیامی مبنی بر اینكه این روابط چرخه حذف بوجود می آورند بدهد و از به روزرسانی بانك جلوگیری كند، در حالی كه به این روابط نیاز داریم و در حین برنامه نویسی كنترل خواهیم كرد كه این چرخه هیچ وقت اتفاق نیفتد و قبل از حذف ركورد تعداد فرزندان اگر بیشتر از صفر بود ركورد حذف نشود.
ولی بازهم اجازه به روزرسانی داده نمی شود بخاطر اینكه ساختار روابط همچنان میگوید چرخه حذف داریم!
برای این كار باید در DbContext تنظیمات زیر را اعمال كنیم تا بفهمانیم عمل حذف به صورت كسكید انجام نشود و مدیریت آن به برنامه نویس داده شود(به زبان ساده تر خطرات چرخه حذف را خودم مدیریت می كنم).
میتوانیم این تنظیم را برای هر مدل انجام دهیم اما معمولا تعداد مدلها در برنامه زیاد هستند و اعمال تنظیمات برای هر مدل به صورت جدا هم زمان و هم كد بیشتری را میخواهد، قطعه كد زیر این تنظیمات را برای تمام روابط اعمال خواهد كرد:
protected override void OnModelCreating(ModelBuilder modelbuilder)
{
foreach (var relationship in modelbuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
base.OnModelCreating(modelbuilder);
}
@WebDevelopmentReferences
#ASP_NET_CORE #EntityFramework #ASPNet
در استفاده از Lazy Loadig مراقب باشید!
Lazy Loading یك امكان در Entity Framework است كه به شما امكان میدهد بدون نگرانی روابط جداول اطلاعات را لود كنید.
به عنوان مثال در پروژه ای به این صورت مدلها پیاده سازی شده اند كه هر دسته بندی تعدادی مطلب دارد، هر مطلب تعدادی نظر و همینطور تعدادی برچسب دارد.
در واكشی به صورت Lazy Loadig شما نگران واكشی مطالب،نظرات و برچسبها بعد از واكشی دسته نیستید و تنها دسته را لود میكنید و به تمام روباط دسترسی دارید:
var category = db.Categories.FirstOrDefault();
var posts = category.Posts.ToList();
foreach(var post in posts)
{
var comments = post.Comments.ToList();
}
همانطور كه دیدید تنها با لود كردن یك دسته به تمام جداول زیرمجموعه آن نیز دسترسی داریم.
در نگاه اول امكان بسیار خوبی به نظر میرسد، بله در مواردی كه اطلاعات كم باشد مشكلی ایجاد نمیكند ولی تصور كنید تعداد بالایی از اطلاعات را در یك درخواست لود كنید در حالی كه تمام اطلاعات موجود در جداول یرمجموعه آن نیز قرار است لود شود در صورتی كه نیازی به آنها ندارید، مثلا هدف تنها نمایش عناوین مطالب یك دسته باشد، در این مثال نیازی به نظرات و برچسب ها نداریم!
در مقابل Lazy Loading امكان Eager Loading را داریم كه در هر كوئری تنها اطلاعات مورد نیاز را لود میكند، یعنی در مثال بالا اگر Lazy Loadig غیرفعال باشد با نوشتن كوئری زیر تنها به دسته دسترسی داریم و جداول مربوط به آن لود نمیشوند:
var category = db.Categories.FirstOrDefault();
كه اگر كد زیر را بنویسیم با خطا روبرو خواهیم شد:
var posts = category.Posts.ToList();
در اصل حالت استاندار نیز به همین صورت می باشد كه تنها اطلاعاتی كه مورد نیاز است را لود كنیم.
حال در مثال بالا وقتی Lazy Loading غیر فعال باشد اگر به مطالب هم نیاز داشته باشیم باید كوئری را به صورت زیر تغییر دهیم:
var categoriy = db.Categories.Include("Posts").FirstOrDefault();
به خاطر داشته باشید كه به صورت پیش فرض حالت Lazy Loading فعال می باشد و برای غیر فعال كردن به دو صورت زیر می توانیم عمل كنیم:
1. تنها در كوئری فعلی:
context.Configuration.LazyLoadingEnabled = false;
2. غیر فعال كردن به صورت عمومی در كلاس DbContext
Configuration.LazyLoadingEnabled = false;
بعد از انجام این عمل برای واكشی اطلاعات مرتبط باید از متد Include استفاده شود.
*** نكته : اگر از EF Core استفاده میكنید نیازی به غیرفعال كردن Lazy Loading ندارید به این دلیل كه این امكان در EF Core وجود ندارد و باید برای واكشی اطلاعات مرتبط از Eager Loading استفاده كنید.
#Lazy_Loadig #Eager_Loading #EntityFramework
@WebDevelopmentReferences
Lazy Loading یك امكان در Entity Framework است كه به شما امكان میدهد بدون نگرانی روابط جداول اطلاعات را لود كنید.
به عنوان مثال در پروژه ای به این صورت مدلها پیاده سازی شده اند كه هر دسته بندی تعدادی مطلب دارد، هر مطلب تعدادی نظر و همینطور تعدادی برچسب دارد.
در واكشی به صورت Lazy Loadig شما نگران واكشی مطالب،نظرات و برچسبها بعد از واكشی دسته نیستید و تنها دسته را لود میكنید و به تمام روباط دسترسی دارید:
var category = db.Categories.FirstOrDefault();
var posts = category.Posts.ToList();
foreach(var post in posts)
{
var comments = post.Comments.ToList();
}
همانطور كه دیدید تنها با لود كردن یك دسته به تمام جداول زیرمجموعه آن نیز دسترسی داریم.
در نگاه اول امكان بسیار خوبی به نظر میرسد، بله در مواردی كه اطلاعات كم باشد مشكلی ایجاد نمیكند ولی تصور كنید تعداد بالایی از اطلاعات را در یك درخواست لود كنید در حالی كه تمام اطلاعات موجود در جداول یرمجموعه آن نیز قرار است لود شود در صورتی كه نیازی به آنها ندارید، مثلا هدف تنها نمایش عناوین مطالب یك دسته باشد، در این مثال نیازی به نظرات و برچسب ها نداریم!
در مقابل Lazy Loading امكان Eager Loading را داریم كه در هر كوئری تنها اطلاعات مورد نیاز را لود میكند، یعنی در مثال بالا اگر Lazy Loadig غیرفعال باشد با نوشتن كوئری زیر تنها به دسته دسترسی داریم و جداول مربوط به آن لود نمیشوند:
var category = db.Categories.FirstOrDefault();
كه اگر كد زیر را بنویسیم با خطا روبرو خواهیم شد:
var posts = category.Posts.ToList();
در اصل حالت استاندار نیز به همین صورت می باشد كه تنها اطلاعاتی كه مورد نیاز است را لود كنیم.
حال در مثال بالا وقتی Lazy Loading غیر فعال باشد اگر به مطالب هم نیاز داشته باشیم باید كوئری را به صورت زیر تغییر دهیم:
var categoriy = db.Categories.Include("Posts").FirstOrDefault();
به خاطر داشته باشید كه به صورت پیش فرض حالت Lazy Loading فعال می باشد و برای غیر فعال كردن به دو صورت زیر می توانیم عمل كنیم:
1. تنها در كوئری فعلی:
context.Configuration.LazyLoadingEnabled = false;
2. غیر فعال كردن به صورت عمومی در كلاس DbContext
Configuration.LazyLoadingEnabled = false;
بعد از انجام این عمل برای واكشی اطلاعات مرتبط باید از متد Include استفاده شود.
*** نكته : اگر از EF Core استفاده میكنید نیازی به غیرفعال كردن Lazy Loading ندارید به این دلیل كه این امكان در EF Core وجود ندارد و باید برای واكشی اطلاعات مرتبط از Eager Loading استفاده كنید.
#Lazy_Loadig #Eager_Loading #EntityFramework
@WebDevelopmentReferences