مشکل N+1 Select در EF

👈 در این مقاله با مشکل N+1 Select در EF آشنا میشیم و نحوه رفع مشکل رو یادمیگیریم.


👁 بازدید : 107

1403/3/16 | 14:7 : تاریخ 📆


 همیشه استفاده بهینه از کوئری‌ها جهت دریافت دیتا از دیتابیس برای وب اپلیکیشن‌ها یک چالش محسوب میشه.در این بین اما کوئری‌های ناکارآمد میتونه باعث تضعیف عملکرد اپلیکیشن ما باشه.نام یکی از این کوئری‌های ناکارآمد N+1 Select یا N+1 Query نام داره، در این مقاله به درک این موضوع می‌پردازیم و راه حل های عملی برای جلوگیری از آن در ASP.NET ارائه می‌کنیم که کارایی و هم مقیاس پذیری برنامه رو افزایش می ده.

درک مشکل  N+1 Select

فرض کنید درEF دو تا آبجکت داریم به نام Supplier و Product که در دیتابیس هر کدوم جدولی هستند.

Supplier:

ID NAME
1 Supplier Name 1
2 Supplier Name 2
... ...

 Product:

ID NAME DESCRIPTION PRICE SUPPLIERID
1 Product 1 Name for Product 1  12.0 1
2 Product 1 Name for Product 2 18.0 2
... ... ... ... ...

حالا برای اینکه دیتاهای دوتا جدول رو کنار هم مرتب داشته باشیم(بر اساس Product) باید چکار کنیم؟ این👇🏻 چطوره!

var products = context.Products.ToList();
var db = new List<Supplier>();
foreach (var item in products)
{
    var mergeRelation = context.Suppliers.Where(a => a.ID == item.SUPPLIERID).ToList();
    db.add(mergeRelation);
}

اگه با این کد موافقی بهت تبریک میگم🎉🎉🎉، تو تونستی به مشکل N+1 Select برسی،باریکلا!🤕

حالا چرا مشکل!؟ این کد دقیقا همون چیزیه که باعث ناکارآمدی وتضعیف عملکرد اپلیکیشن ما میشه، فرض کنید تعداد رکوردهای ثبت شده در دو جدول هزاران یا ملیونها رکورد باشه، در این صورت برنامه ما به تعداد هر ردیف، آی دی Product و Supplier رو باید برسی کنه، یعنی اگر ما nتا ردیف داشته باشیم، nبار این برسی انجام میشه و تمام زحمت بر دوش اپلیکیشن ماست، و جدا از این تعداد خط کدهای ما هم زیاده و میتونیم تو یک خط، کار رو ایزی دربیاریم.

 

راه حل:

در ابتدا باید با مفهوم LazyLoading آشنا بشید. دقیقا برای دریافت دیتا به روش بالا LazyLoading گفته میشه.یعنی شما دیتای یک جدول رو بدون درنظر گرفتن روابط اون جدول با جداول دیگه دریافت کنید.

+ سوال : حالا آیا این روش به نحوه کدنویسی ما ربط داره؟ 

- جواب : بله، خیلی. اگر در تعریف انتیتی در کنار Navigation Property از کلمه virtual استفاده کنید EF برای ارسال و دریافت دیتا به روش LazyLoading عمل میکنه.مثل:

public class Supplier
{
    public int ID { get; set; }
    public string NAME { get; set; }
}
public class Product
{      
    public int ID { get; set; }
    public string NAME { get; set; }
    public string DESCRIPTION { get; set; }
    public int PRICE { get; set; }

    public int SUPPLIERID { get; set; }
    public virtual Supplier Supplier { get; set; }

}

 

1- LazyLoading رو غیر فعال کن:

کلمه virtual رو از Navigation Property حذف کن و کد زیر رو در کانستراکتور کلاس کانتکست قرار بده.

this.ChangeTracker.LazyLoadingEnabled = false;

 

2- از EagerLoading استفاده کن:

با استفاده از متد Include در EF میتونید بگید جدولی که لازم دارید با کدوم جدول که در ارتباطه براتون برگرده.

var orders = context.Products.Include(o => o.Suppliers).ToList();

3- از Selective Loading استفاده کن:

با استفاده از متد Select در EF میتونید انتخاب کنید چه مقادیری از دو جدول براتون برگرده.

var orderDetails = context.Products.Select(o => new
{
    o.ID,
    o.NAME,
    o.PRICE,
    Suppliers = o.Suppliers.Select(d => new { d.ID, d.NAME})
}).ToList();

 

نتیجه گیری :

با استراتژی مناسب، به راحتی میشه از مشکل N+1 Select اجتناب کرد.با درک درست از انتیتی‌ها،عملکرد و روابط آن‌ها و استفاده مؤثر از ویژگی‌های Entity Framework، می‌تونید مطمئن بشید که برنامه‌تون کارآمده و عملکرد بهینه خواهد داشت.

 همیشه استفاده بهینه از کوئری‌ها جهت دریافت دیتا از دیتابیس برای وب اپلیکیشن‌ها یک چالش محسوب میشه.در این بین اما کوئری‌های ناکارآمد میتونه باعث تضعیف عملکرد اپلیکیشن ما باشه.نام یکی از این کوئری‌های ناکارآمد N+1 Select یا N+1 Query نام داره، در این مقاله به درک این موضوع می‌پردازیم و راه حل های عملی برای جلوگیری از آن در ASP.NET ارائه می‌کنیم که کارایی و هم مقیاس پذیری برنامه رو افزایش می ده.

درک مشکل  N+1 Select

فرض کنید درEF دو تا آبجکت داریم به نام Supplier و Product که در دیتابیس هر کدوم جدولی هستند.

Supplier:

ID NAME
1 Supplier Name 1
2 Supplier Name 2
... ...

 Product:

ID NAME DESCRIPTION PRICE SUPPLIERID
1 Product 1 Name for Product 1  12.0 1
2 Product 1 Name for Product 2 18.0 2
... ... ... ... ...

حالا برای اینکه دیتاهای دوتا جدول رو کنار هم مرتب داشته باشیم(بر اساس Product) باید چکار کنیم؟ این👇🏻 چطوره!

var products = context.Products.ToList();
var db = new List<Supplier>();
foreach (var item in products)
{
    var mergeRelation = context.Suppliers.Where(a => a.ID == item.SUPPLIERID).ToList();
    db.add(mergeRelation);
}

اگه با این کد موافقی بهت تبریک میگم🎉🎉🎉، تو تونستی به مشکل N+1 Select برسی،باریکلا!🤕

حالا چرا مشکل!؟ این کد دقیقا همون چیزیه که باعث ناکارآمدی وتضعیف عملکرد اپلیکیشن ما میشه، فرض کنید تعداد رکوردهای ثبت شده در دو جدول هزاران یا ملیونها رکورد باشه، در این صورت برنامه ما به تعداد هر ردیف، آی دی Product و Supplier رو باید برسی کنه، یعنی اگر ما nتا ردیف داشته باشیم، nبار این برسی انجام میشه و تمام زحمت بر دوش اپلیکیشن ماست، و جدا از این تعداد خط کدهای ما هم زیاده و میتونیم تو یک خط، کار رو ایزی دربیاریم.

 

راه حل:

در ابتدا باید با مفهوم LazyLoading آشنا بشید. دقیقا برای دریافت دیتا به روش بالا LazyLoading گفته میشه.یعنی شما دیتای یک جدول رو بدون درنظر گرفتن روابط اون جدول با جداول دیگه دریافت کنید.

+ سوال : حالا آیا این روش به نحوه کدنویسی ما ربط داره؟ 

- جواب : بله، خیلی. اگر در تعریف انتیتی در کنار Navigation Property از کلمه virtual استفاده کنید EF برای ارسال و دریافت دیتا به روش LazyLoading عمل میکنه.مثل:

public class Supplier
{
    public int ID { get; set; }
    public string NAME { get; set; }
}
public class Product
{      
    public int ID { get; set; }
    public string NAME { get; set; }
    public string DESCRIPTION { get; set; }
    public int PRICE { get; set; }

    public int SUPPLIERID { get; set; }
    public virtual Supplier Supplier { get; set; }

}

 

1- LazyLoading رو غیر فعال کن:

کلمه virtual رو از Navigation Property حذف کن و کد زیر رو در کانستراکتور کلاس کانتکست قرار بده.

this.ChangeTracker.LazyLoadingEnabled = false;

 

2- از EagerLoading استفاده کن:

با استفاده از متد Include در EF میتونید بگید جدولی که لازم دارید با کدوم جدول که در ارتباطه براتون برگرده.

var orders = context.Products.Include(o => o.Suppliers).ToList();

3- از Selective Loading استفاده کن:

با استفاده از متد Select در EF میتونید انتخاب کنید چه مقادیری از دو جدول براتون برگرده.

var orderDetails = context.Products.Select(o => new
{
    o.ID,
    o.NAME,
    o.PRICE,
    Suppliers = o.Suppliers.Select(d => new { d.ID, d.NAME})
}).ToList();

 

نتیجه گیری :

با استراتژی مناسب، به راحتی میشه از مشکل N+1 Select اجتناب کرد.با درک درست از انتیتی‌ها،عملکرد و روابط آن‌ها و استفاده مؤثر از ویژگی‌های Entity Framework، می‌تونید مطمئن بشید که برنامه‌تون کارآمده و عملکرد بهینه خواهد داشت.

🗨️دیدگاه‌ها


مشکل N+1 Select در EF

هنوز دیدگاهی ثبت نشده

تمامی‌حقوق‌مادی‌ومعنوی‌این‌سایت‌برای‌یادمیگیریم‌محفوظ است.