موجودیت‌های مرتبط در Entity Framework

EF از سه روش بارگیری با اشتیاق (Eager Loading)، بارگیری تنبل (Lazy Loading) و بارگیری صریح (Explicit Loading) پشتیبانی می‌کند.

۱. بارگیری مشتاقانه (Eagerly Loading)

Eager Loading فرایندی است که طی آن یک کوئری برای یک نوع موجودیت، موجودیت‌های مرتبط با آن را به عنوان بخشی از کوئری نیز Load می‌کند. برای استفاده از Eager Loading باید از متد Include استفاده کنیم. برای مثال، کوئری زیر همه‌ی بلاگ‌ها و پست‌های مرتبط با هر وبلاگ را Load خواهد کرد:

using (var context = new BloggingContext()) 
{ 
    // دریافت همه وبلاگ‌ها همراه پست‌های مرتبط‌شان
    var blogs1 = context.Blogs 
                          .Include(b => b.Posts) 
                          .ToList(); 
 
    // دریافت یک وبلاگ و پست‌های مربوط به آن
    var blog1 = context.Blogs 
                        .Where(b => b.Name == "ADO.NET Blog") 
                        .Include(b => b.Posts) 
                        .FirstOrDefault(); 
 
    // دریافت همه وبلاگ‌ها همراه پست‌های مرتبط‌شان از طریق رشته برای مشخص نمودن رابطه
    var blogs2 = context.Blogs 
                          .Include("Posts") 
                          .ToList(); 
 
    // دریافت یک وبلاگ‌ به‌خصوص همراه پست‌های مرتبط‌اش از طریق رشته برای مشخص نمودن رابطه
    var blog2 = context.Blogs 
                        .Where(b => b.Name == "ADO.NET Blog") 
                        .Include("Posts") 
                        .FirstOrDefault(); 
}

توجه کنید که Include یک متد توسعه یافته (Extension Method) در فضای نام System.Data.Entity است. برای استفاده از این متد باید این فضای نام را به برنامه اضافه کنید.

۱-۱. بارگیری مشتاقانه چند سطحی (Eager Loading Multiple Levels)

این امکان وجود دارد که چندین سطح مختلف از Entity های مرتبط را Load کنیم. مثال زیر روش انجام این‌کار را برای خواص راهبردی (Navigation Property) نوع Collection و نوع Reference را بیان می‌کند:

using (var context = new BloggingContext()) 
{ 
    // دریافت تمامی وبلاگ‌ها به همراه همه‌ی پست‌ها و همه‌ی کامنت‌های پست‌ها
    var blogs1 = context.Blogs 
                       .Include(b => b.Posts.Select(p => p.Comments)) 
                       .ToList(); 
 
    // دریافت تمام پروفایل‌‌ کاربران و آواتار مربوط به هرکدام
    var users1 = context.Users 
                        .Include(u => u.Profile.Avatar) 
                        .ToList(); 
 
    // دریافت همه‌ی وبلاگ‌ها به همراه تمامی پست‌های آن‌ها و کامنت‌های مربوطه از طریق معرفی رابطه به صورت رشته‌ای
    var blogs2 = context.Blogs 
                       .Include("Posts.Comments") 
                       .ToList(); 
 
    // دریافت پروفایل تمامی کاربران و آواتار مربوط به آن‌ها از طریق معرفی رابطه به صورت رشته‌ای
    var users2 = context.Users 
                        .Include("Profile.Avatar") 
                        .ToList(); 
}

توجه کنید که در زمان نگارش این مقاله امکان فیلتر نمودن موجودیت‌های مرتبط، برای Load وجود ندارد و متد Include همیشه تمام موجودیت‌های مرتبط را خواهد آورد.

۲. بارگیری تنبل (Lazy Loading)

Lazy Loading فرایندی است که طی آن یک Entity یا یک مجموعه از Entityها به صورت خودکار زمانی که برای اولین بار موجودیت (های) یک خاصیت (Property) مورد دستیابی قرار می‌گیرد از دیتابیس Load‌ خواهد (خواهند) شد. زمانی که از موجودیت نوع POCO استفاده می کنید Lazy Loading از طریق ایجاد یک نمونه از نوع‌های پروکسی مشتق شده و سپس override نمودن پراپرتی‌های virtual برای اضافه نمودن قابلیت Loading قابل استفاده هستند.

به‌طور مثال زمانی که از کلاس موجودیت Blog تعریف شده در زیر استفاده می‌کنید، همه‌ی Post مرتبط، زمانی که برای اولین بار از خاصیت راهبردی Posts استفاده می‌کنید، Load خواهند شد:

public class Blog 
{  
    public int BlogId { get; set; }  
    public string Name { get; set; }  
    public string Url { get; set; }  
    public string Tags { get; set; }  
 
    public virtual ICollection<Post> Posts { get; set; }  
}

۱-۲. خاموش نمودن Lazy Loading برای خاصیت‌های راهبردی خاص

برای خاموش نمودن خاصیت Lazy Loading برای مثال در خاصیت مجموعه‌ای Posts، کافی است که عبارت virtual را از آن حذف کنیم:

public class Blog 
{  
    public int BlogId { get; set; }  
    public string Name { get; set; }  
    public string Url { get; set; }  
    public string Tags { get; set; }  
 
    public ICollection<Post> Posts { get; set; }  
}

نکته: امکان Load مجموعه Posts پس از این کار همچنان از طریق Eager Loading یا روش Explicitly Loading (توضیح در ادامه) وجود خواهد داشت.

۲-۲. خاموش کردن کامل Lazy Loading برای تمامی موجودیت‌ها

برای خاموش کردن کامل Lazy Loading می‌توان از flag مربوط به این‌کار در خاصیت Configuration کمک گرفت. برای مثال:

public class BloggingContext : DbContext 
{ 
    public BloggingContext() 
    { 
        this.Configuration.LazyLoadingEnabled = false; 
    } 
}

نکته: امکان Load همه‌ی موجودیت‌های مرتبط پس از این کار همچنان از طریق Eager Loading یا روش Explicitly Loading (توضیح در ادامه) وجود خواهد داشت.

۳. بارگیری با صراحت (Explicity Loading)

حتی زمانی که Lazy Loading‌ خاموش است امکان استفاده از آن برای موجودیت‌های مرتبط وجود دارد. اما برای این‌کار باید با یک فراخوانی صریح انجام شود. برای این‌کار باید از متد Load پس از فراخوانی موجودیت‌های مرتبط استفاده کنید. برای مثال:

using (var context = new BloggingContext()) 
{ 
    var post = context.Posts.Find(2); 
 
    // دریافت وبلاگ مربوط به پست مشخص شده
    context.Entry(post).Reference(p => p.Blog).Load(); 
 
    //دریافت وبلاگ مربوط به پست مشخص شده با کمک رشته  
    context.Entry(post).Reference("Blog").Load(); 
 
    var blog = context.Blogs.Find(1); 
 
    // دریافت همه پست‌های مرتبط با یک وبلاگ 
    context.Entry(blog).Collection(p => p.Posts).Load(); 
 
    //دریافت پست‌های یک وبلاگ از طریق معرفی یک رشته برای رابطه
    context.Entry(blog).Collection("Posts").Load(); 
}

نکته: توجه کنید که متد Reference زمانی که یک موجودیت دارای خاصیت راهبردی به یک موجودیت منفرد است استفاده می‌شود (مثل زمانی که یک رابطه یک به یک موجود باشد). همچنین زمانی که یک موجودیت از کلاس فعلی دارای یک خاصیت راهبردی به مجموعه از موجودیت‌ها را دارد (مثل زمانی که رابطه یک به چند داریم) از متد Collection استفاده می‌کنیم.

۱-۳. فیلتر نمودن اطلاعات زمانی که از روش Explicit Loading برای موجودیت‌های مرتبط استفاده شده باشد.

متد Query امکان دستیابی به کوئری که Entity Framework قرار است از آن برای دستیابی به موجودیت‌های مرتبط استفاده کند را فراهم می‌کند. پس از آن می‌ةوان از دستورات LINQ برای ایجاد هرگونه فیلتر دلخواه بر روی‌ کوئری پیش از اجرای آن با کمک یکی از متدهای توسعه یافته LINQ مانند ToList یا Load یا ... استفاده نمود.

از متد Query می‌توان در هر دو حالت خواص راهبردی Reference یا Collection استفاده نمود ولی این کار بیشتر برای خواص راهبردی Collection برای فیلتر کردن بخشی از مجموعه که قرار است Load شود کاربرد دارد. برای مثال:

using (var context = new BloggingContext()) 
{ 
    var blog = context.Blogs.Find(1); 
 
    //دریافت همه‌ی پست‌های دارای تگ
    // 'entity-framework' 
    // برای وبلاگ مشخص شده
    context.Entry(blog) 
        .Collection(b => b.Posts) 
        .Query() 
        .Where(p => p.Tags.Contains("entity-framework") 
        .Load(); 
 
    //دریافت همه‌ی پست‌های دارای تگ
    // 'entity-framework' 
    // برای وبلاگ مشخص شده از طریق مشخص نمودن رابطه با رشته 
    context.Entry(blog) 
        .Collection("Posts") 
        .Query() 
        .Where(p => p.Tags.Contains("entity-framework") 
        .Load(); 
}

زمانی که از متد Query بهره می‌برید، بهتر است ویژگی Lazy Loading را به‌طور کامل برای خواص راهبردی خاموش کنید. به این دلیل که ممکن است کل collection مرتبط با خواص راهبردی یک موجودیت پیش یا پس از ایجاد فیلتر بر روی کوئری به طور خودکار توسط Lazy Loading لود گردد.

نکته: زمانی که ارتباط را به‌جای استفاده از عبارات lambda از یک string ساده مشخص می‌کنید، خروجی IQueryable جنریک نخواهد بود و معمولا یک تبدیل با استفاده از متد Cast در این‌جا نیاز خواهد بود.

استفاده از متد Query برای شمارش موجودیت‌های مرتبط بدون Load آن‌ها

گاهی اوقات لازم است بدانیم به یک موجودیت چه تعداد موجودیت وابسته (مرتبط) است بدون آنکه نیاز باشد تمام آن‌ها را از دیتابیس فراخوانی کنیم. به این دلیل که فراخوانی همه‌ی موجودیت‌های مرتبط به هیچ عنوان مقرون به صرفه نخواهد بود و هزینه‌ی زیادی خواهد داشت، در اینجا EF یک امکان بسیار عالی را در اختیار ما قرار داده است. برای این کار کافی است از متد Query و متد Count مربوط به LINQ به‌طور همزمان و به شکل زیر استفاده کنیم:

using (var context = new BloggingContext()) 
{ 
    var blog = context.Blogs.Find(1); 
 
    // شمارش تعداد پست‌های یک وبلاگ  
    var postCount = context.Entry(blog) 
                          .Collection(b => b.Posts) 
                          .Query() 
                          .Count(); 
}



  • Book.mark.hu
  • co.mments
  • De.lirio.us
  • del.icio.us
  • Digg
  • DotNetKicks
  • E-Mail
  • Facebook
  • feedmelinks
  • Google
  • LinkedIn
  • msdn Social
  • MyShare
  • Slashdot
  • StumbleUpon
  • TwitThis
  • Tumblr
  • Yahoo! Buzz
  • Yahoo! MyWeb
  • Print
امکان ارسال نظر برای این موضوع وجود ندارد