نحوهی اعمال فیلتر بر روی روی دادههای مرتبط (جوینشده) در Entity Framework Core
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۱/۲۴ ۱۹:۰۱
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
Whereمعمولی:Where روی کل مجموعه دادههای اصلی (root entity) اعمال میشود، اما Filtered Includes روی دادههای مرتبط داخل Include کار میکند.var query = dbContext.Entity
.Include(e => e.RelatedEntities.Where(condition));Entity: جدول یا موجودیت اصلی.RelatedEntities: مجموعه دادههای مرتبط (مثل یک لیست در رابطه یکبهچند).condition: شرط فیلتری که روی دادههای مرتبط اعمال میشود.public class Order
{
public int Id { get; set; }
public decimal TotalAmount { get; set; }
public List<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
}Quantity) بیشتر از ۲ است لود کنیم.var query = dbContext.Orders
.Select(o => new
{
Order = o,
OrderItems = o.OrderItems.Where(oi => oi.Quantity > 2).ToList()
});Order.var query = dbContext.Orders
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 2));Quantity > 2 دارند در مجموعه OrderItems هر سفارش قرار میگیرند.Order و OrderItems) و چندبهچند (با استفاده از جدول واسطه) کار میکند.Includeهای دیگر ترکیب کنید:var query = dbContext.Orders
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 2))
.Include(o => o.Customer);ThenInclude استفاده کنید:var query = dbContext.Orders
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 2))
.ThenInclude(oi => oi.Product);Select یا کوئریهای خام SQL باشد.Load، که نیاز به چندین درخواست به دیتابیس دارد.var query = dbContext.Orders
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 2));SELECT o.*, oi.* FROM Orders o LEFT JOIN OrderItems oi ON o.Id = oi.OrderId AND oi.Quantity > 2
public class Order
{
public int Id { get; set; }
public decimal TotalAmount { get; set; }
public DateTime OrderDate { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public List<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}var query = dbContext.Orders
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 3))
.ThenInclude(oi => oi.Product.Where(p => p.Category.Name == "Electronics"));Include اول فقط آیتمهای سفارش با Quantity > 3 را لود میکند.ThenInclude روی محصولات مرتبط با آیتمهای فیلترشده اعمال میشود و فقط محصولاتی که در دستهبندی "Electronics" هستند را شامل میشود.SELECT o.*, oi.*, p.* FROM Orders o LEFT JOIN OrderItems oi ON o.Id = oi.OrderId AND oi.Quantity > 3 LEFT JOIN Products p ON oi.ProductId = p.Id INNER JOIN Categories c ON p.CategoryId = c.Id AND c.Name = 'Electronics'
var query = dbContext.Orders
.Where(o => o.OrderDate.Year == 2024)
.Include(o => o.OrderItems.Where(oi => oi.Price > 50))
.Include(o => o.Customer);Where اول روی جدول اصلی (Orders) اعمال میشود و فقط سفارشات سال ۲۰۲۴ را انتخاب میکند.Include اول آیتمهای سفارش با قیمت بیش از ۵۰ را لود میکند.Include دوم اطلاعات مشتری را برای هر سفارش اضافه میکند.Where قبل از Include باعث میشود دادههای کمتری از جدول اصلی لود شوند، که بهینهتر است.var query = dbContext.Orders
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 5 && oi.Price > 100))
.Where(o => o.OrderItems.Any(oi => oi.Quantity > 5 && oi.Price > 100));Include آیتمهایی را لود میکند که هم Quantity > 5 و هم Price > 100 باشند.Where روی جدول اصلی تضمین میکند که فقط سفارشاتی برگردانده شوند که حداقل یک آیتم با این شرایط داشته باشند (بدون این شرط، سفارشات بدون آیتم هم ممکن است برگردانده شوند).SELECT o.*, oi.*
FROM Orders o
LEFT JOIN OrderItems oi ON o.Id = oi.OrderId AND oi.Quantity > 5 AND oi.Price > 100
WHERE EXISTS (
SELECT 1
FROM OrderItems oi2
WHERE oi2.OrderId = o.Id AND oi2.Quantity > 5 AND oi2.Price > 100
)Customer و Order از طریق یک جدول واسطه (مثلاً CustomerOrder) داریم. میخواهیم مشتریانی را لود کنیم که سفارشاتشان در سال ۲۰۲۴ باشد، و فقط آن سفارشات را شامل کنیم.var query = dbContext.Customers
.Include(c => c.Orders.Where(o => o.OrderDate.Year == 2024));Include فقط سفارشاتی را لود میکند که در سال ۲۰۲۴ ثبت شدهاند.ThenInclude برای دسترسی به ویژگیهای جدول واسطه داشته باشید.var query = dbContext.Orders
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 2))
.Select(o => new
{
OrderId = o.Id,
TotalAmount = o.TotalAmount,
OrderItems = o.OrderItems.OrderBy(oi => oi.Price).Select(oi => new
{
oi.Id,
oi.Quantity,
oi.Price
})
});Include آیتمهای با Quantity > 2 را لود میکند.OrderBy آیتمها را بر اساس قیمت مرتب میکند.Select خروجی را به یک مدل سبکتر محدود میکند تا فقط فیلدهای موردنیاز برگردانده شوند.Select استفاده کردیم، Filtered Includes همچنان نقش اصلی را در فیلتر کردن دادههای مرتبط ایفا میکند.AsNoTracking برای بهبود عملکرد استفاده کنید:var query = dbContext.Orders
.AsNoTracking()
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 3));&&, ||) پشتیبانی میکند، اما برای شرایط بسیار پیچیده (مثل زیرکوئریهای تودرتو)، ممکن است نیاز به استفاده از کوئریهای خام یا روشهای دیگر داشته باشید.Include، میتوانید از AsSplitQuery استفاده کنید تا کوئری به چندین درخواست SQL تقسیم شود:var query = dbContext.Orders
.AsSplitQuery()
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 3))
.Include(o => o.Customer);var query = dbContext.Orders
.Include(o => o.OrderItems.Where(oi => oi.Product != null && oi.Product.Price > 100));Select، کوئریهای خام، یا Split Queries داشته باشید.