در Entity Framework Core، مکانیزم Relational Fixup به فرآیند خودکار مقداردهی و اتصال Navigation Propertyها (مانند post.Blog یا blog.Posts) بین موجودیتها (Entities) اشاره دارد که توسط یک نمونه DbContext یکسان رهگیری (Track) میشوند. 🔗
هدف اصلی این مکانیزم، حفظ سازگاری گراف اشیاء (Object Graph) در حافظه با روابط تعریفشده در پایگاه داده، برای موجودیتهای رهگیریشده است.
نحوه عملکرد Relational Fixup (رفتار پیشفرض با رهگیری):
وقتی EF Core یک موجودیت را رهگیری میکند (از طریق کوئریهای پیشفرض، Find(), Load(), Add(), Attach(), Update()), آن را در Change Tracker خود ثبت میکند. Change Tracker اطلاعات کلید اصلی (PK) و کلید خارجی (FK) را نگه میدارد.
Relational Fixup به عنوان بخشی از فرآیند رهگیری رخ میدهد:
مثال (با رهگیری - Fixup انجام میشود):
Blog با ID=1 رهگیری میشود
var blog = context.Blogs.Find(1);
Posts مرتبط با آن Blog لود و رهگیری میشوند
var posts = context.Posts.Where(p => p.BlogId == 1).ToList();
✅ Fixup خودکار انجام شده:
foreach(var post in posts)
{
post.Blog به شیء blog اشاره میکند
Console.WriteLine($"Post '{post.Title}' belongs to Blog '{post.Blog?.Url}'");
}
blog.Posts شامل اشیاء post لود شده است (اگر Posts مقداردهی اولیه شده باشد)
Console.WriteLine($"Blog '{blog.Url}' has {blog.Posts?.Count} posts.");
تأثیر AsNoTracking() (رفتار بدون Fixup): 🎯
اینجاست که تفاوت کلیدی مشخص میشود. وقتی شما از متد AsNoTracking() در کوئریهای خود استفاده میکنید:
EF Core موجودیتهای برگشتی از دیتابیس را رهگیری نمیکند. ❌
در نتیجه، این موجودیتها وارد Change Tracker نمیشوند.
چون موجودیتها رهگیری نمیشوند، مکانیزم Relational Fixup اصلاً اجرا نمیشود. 🚫
حتی اگر از Include() همراه با AsNoTracking() استفاده کنید، EF Core دادههای مرتبط را در سطح SQL با هم JOIN میکند، اما در سطح اشیاء #C، هیچ تلاشی برای مقداردهی Navigation Propertyها انجام نمیدهد.
مثال (با AsNoTracking() - Fixup انجام نمیشود):
لود Blog و Posts بدون رهگیری
var blogNoTrack = context.Blogs
.Include(b => b.Posts) Include برای JOIN در SQL
.AsNoTracking() عدم رهگیری و عدم Fixup
.FirstOrDefault(b => b.BlogId == 1);
❌ Fixup انجام نشده:
if (blogNoTrack != null)
{
blogNoTrack.Posts احتمالا null یا خالی خواهد بود (بستگی به پیادهسازی دارد، اما EF Core آن را پر نکرده)
Console.WriteLine($"Blog (No Tracking) '{blogNoTrack.Url}' - Posts count: {blogNoTrack.Posts?.Count ?? 0}"); احتمالا 0 یا خطا اگر Posts null باشد
if (blogNoTrack.Posts != null)
{
foreach(var post in blogNoTrack.Posts)
{
post.Blog هم null خواهد بود، چون Fixup انجام نشده
Console.WriteLine($"Post '{post.Title}' - Blog reference is null: {post.Blog == null}"); // <- True
}
}
}
چه زمانی Relational Fixup (نوع با رهگیری) رخ میدهد؟ ⏱️
نکات کلیدی و پیشنیازها: 📝
نتیجهگیری: ✨
Relational Fixup یک قابلیت مفید برای حفظ سازگاری روابط در موجودیتهای رهگیریشده است. در مقابل، استفاده از AsNoTracking() با هدف افزایش پرفورمنس، هزینه عدم اجرای Fixup و عدم مقداردهی خودکار Navigation Propertyها را به همراه دارد. درک این تفاوت برای انتخاب روش مناسب کوئری زدن در سناریوهای مختلف ضروری است.