دوره آموزشی Entity FrameWork Core - قسمت 23
در Lazy Loading یک موجودیت فرزند تنها زمانی بارگیری میشود که برای اولین بار به آن دسترسی داشته باشید. این به سادگی بارگذاری داده های مرتبط را به تاخیر می اندازد، تا زمانی که شما آن را درخواست کنید.
بارگذاری تنبل داده ها الگویی است که به موجب آن بازیابی داده ها از پایگاه داده تا زمانی که واقعاً مورد نیاز باشد به تعویق می افتد. این چیز خوبی به نظر می رسد، و در برخی سناریوها، این می تواند به بهبود عملکرد یک برنامه کمک کند. در سناریوهای دیگر، می تواند عملکرد یک برنامه را به طور قابل توجهی کاهش دهد، به ویژه در برنامه های
web application. چرا که بارگذاری تنبل می تواند باعث ایجاد رفت و برگشت های اضافی غیر ضروری در پایگاه داده شود (به اصطلاح مشکل N+1)، و باید مراقب بود تا از این امر جلوگیری شود.
مشکل N+1
مشکل N+1 مشکلی است که در اکثر ORMها یا ابزارهای Object Relational Mapper وجود دارد. مشکل N+1 در نتیجه به اصطلاح "بارگذاری تنبل" رخ می دهد. بارگیری تنبل داده ها از پایگاه داده زمانی اتفاق می افتد که یک پرس و جو برای یک رکورد والد و سپس یک پرس و جو اضافی برای هر رکورد فرزند ایجاد شود.
بارگذاری تنبل Lazy Loading به این معنی است که داده های مرتبط از پایگاه داده زمانی که به
navigation property دسترسی پیدا می کند بارگیری می شود.
بارگذاری تنبل را می توان به دو روش فعال کرد:
ساده ترین راه برای استفاده از Lazy Loading، نصب بسته Microsoft.EntityFrameworkCore.Proxies و فعال کردن آن با فراخوانی به UseLazyLoadingProxies است.
پراکسی ها اشیایی هستند که از موجودیت های شما مشتق می شوند و در زمان اجرا توسط Entity Framework Core تولید می شوند. این پراکسیها رفتاری به آنها اضافه شده است که منجر به درخواستهای پایگاه داده در صورت نیاز برای بارگیری navigation propertyها در صورت تقاضا میشود.
مثلا:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder .UseLazyLoadingProxies() .UseSqlServer(myConnectionString);
یا هنگام استفاده از AddDbContext:
.AddDbContext<BloggingContext>( b => b.UseLazyLoadingProxies() .UseSqlServer(myConnectionString));
سپس EF Core بارگذاری تنبل را برای هر navigation property که میتواند نادیده گرفته شود، فعال میکند - یعنی باید virtual
و روی کلاسی باشد که بتوان از آن به ارث برد. به عنوان مثال، در موجودیتهای زیر، navigation propertyهای Post.Blog و Blog.Posts با تنبلی بارگذاری میشوند.
public class Blog { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<Post> Posts { get; set; } } public class Post { public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } public virtual Blog Blog { get; set; } }
ILazyLoader
:خوب Lazy-loading بدون پراکسی با تزریق سرویس ILazyLoader به یک موجودیت کار می کند، همانطور که در Entity Type Constructors توضیح داده شده است.
رابط ILazyLoader مؤلفهای را نشان میدهد که مسئول بارگیری navigation propertyها است، اگر قبلاً بارگذاری نشده باشند.
این رویکرد تولید پراکسیهایی را که در همه پلتفرمها پشتیبانی نمیشوند، دور میزند.
بسته Microsoft.EntityFrameworkCore.Abstractions را در پروژه خود نصب کنید.
مثلا:
public class Blog { private ICollection<Post> _posts; public Blog() { } private Blog(ILazyLoader lazyLoader) { LazyLoader = lazyLoader; } private ILazyLoader LazyLoader { get; set; } public int Id { get; set; } public string Name { get; set; } public ICollection<Post> Posts { get => LazyLoader.Load(this, ref _posts); set => _posts = value; } } public class Post { private Blog _blog public Post() { } private Post(ILazyLoader lazyLoader) { LazyLoader = lazyLoader; } private ILazyLoader LazyLoader { get; set; } public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } public Blog Blog { get => LazyLoader.Load(this, ref _blog); set => _blog = value; } }
این روش نیازی به virtual navigation properties ندارد. با این حال، نیاز به ارجاع به سرویس ILazyLoader دارد که در بسته Microsoft.EntityFrameworkCore.Abstractions تعریف شده است. این بسته حاوی حداقل مجموعه ای از typeهاست با این حال، برای اجتناب از وابستگی کامل به بستههای EF Core، میتوان متد ILazyLoader.Load را به عنوان نماینده تزریق کرد. مثلا:
public class Blog { private ICollection<Post> _posts; public Blog() { } private Blog(Action<object, string> lazyLoader) { LazyLoader = lazyLoader; } private Action<object, string> LazyLoader { get; set; } public int Id { get; set; } public string Name { get; set; } public ICollection<Post> Posts { get => LazyLoader.Load(this, ref _posts); set => _posts = value; } } public class Post { private Blog _blog; public Post() { } private Post(Action<object, string> lazyLoader) { LazyLoader = lazyLoader; } private Action<object, string> LazyLoader { get; set; } public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } public Blog Blog { get => LazyLoader.Load(this, ref _blog); set => _blog = value; } }
کد بالا از Load extension method استفاده می کند تا با استفاده از delegate کمی تمیزتر شود:
public static class PocoLoadingExtensions { public static TRelated Load<TRelated>( this Action<object, string> loader, object entity, ref TRelated navigationField, [CallerMemberName] string navigationName = null) where TRelated : class { loader?.Invoke(entity, navigationName); return navigationField; } }
پارامتر سازنده برای نماینده بارگذاری تنبل باید "lazyLoader" نامیده شود.
چه از پراکسی ها و چه از رابط ILazyLoader استفاده کرده باشید، بارگذاری تنبل اکنون در برنامه شما فعال است و به محض ارجاع به موجودیت های وابسته در یک رابطه انجام می شود.
زمانی که ارتباط بین موجودیت ها یک رابطه یک به چند باشد و مطمئن باشید که موجودیت های مرتبط فورا مورد استفاده قرار نخواهند گرفت، بارگذاری تنبل مفید است.
این به کاهش زمان راهاندازی برنامه، استفاده کمتر از حافظه و کاهش بار روی DBMS به دلیل مقدار کمی بار پرس و جو در سرور کمک میکند.
آنچه از Lazy Loading می توان نتیجه گرفت این است که این ویژگی در برخی سناریوها مفید است، در غیر این صورت این ویژگی، می تواند بر روی کاهش عملکرد تاثیرگذار باشد.
توصیه این است که از بارگذاری تنبل استفاده نکنید، مگر اینکه مطمئن باشید که راه حل بهتری است. به همین دلیل است که (برخلاف نسخه های اولیه EF) بارگذاری تنبل به طور پیش فرض در Entity Framework Core فعال نیست.
بیشتر بخوانید : دوره آموزشی Entity FrameWork Core
بیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Core
https://zarinp.al/farshidazizi