Include 用于获取表的完整行,包括主键和外键。
通常获取表的完整行效率不高。例如,假设您有一个包含学校和学生的数据库。 Schools和Student之间存在一对多的关系:每个School有零个或多个Student,每个Student只上一所School,即外键所指的School。
如果您获取 School [10] 及其 2000 个学生,那么每个 Student 将有一个值为 10 的外键 SchoolId。如果您使用 Include 并获取完整的 Student 行,您将传输该值 10 超过 2000 次。多么浪费处理能力。
一个 DbContext 有一个 ChangeTracker 对象。每当您在不使用 Select 的情况下获取数据时,如果您获取完整的行,那么获取的行将与它的 Clone 一起存储在 ChangeTracker 中。您会得到对克隆的引用(或原始的,没关系)。当您更改获取数据的属性时,您会更改 Clone 中的值。当您调用SaveChanges 时,ChangeTracker 中所有原件的所有属性的值都与 Clone 中的值进行比较。更改的项目在数据库中更新。
因此,如果您获取 School [10] 及其学生,您不仅会获取比以往更多的数据,而且还会将所有这些学生与克隆的学生一起存储在 ChangeTracker 中。如果您为完全不同的东西(例如学校的电话号码)调用 SaveChanges,那么所有学生都会与他们的克隆进行逐个属性的值比较。
每当您使用 Entity Framework 获取数据时,请始终使用 Select,并仅选择您实际计划使用的属性。仅获取完整的行,仅在计划更新获取的数据时使用 Include。
使用 Select 也可以解决您的问题:
int ownerId = ...
IEnumerable<Product> products = ...
var Owner = db.Owners.Where(owner => owner.ID == ownerId)
.Select(owner => new
{
// Select only the Owner properties that you actually plan to use
Id = owner.Id,
Name = owner.Name,
// get the Products of this Owner that are in variable products
Products = owner.Products
.Where(product => products.Any(p => p.ProductId == product.ProductId)
.Select(product => new
{
// Select only the Product properties that you plan to use
Id = product.Id,
Price = product.Price,
...
// No need to fetch the foreign key, you already fetched the value
// OwnerId = product.OwnerId,
})
.ToList(),
...
})
.FirstOrDefault();
我使用了自动类型 (new {...})。如果您真的想创建所有者和属性,请使用:
var Owner = db.Owners.Where(...)
.Select(owner => new Owner
{
Id = owner.Id,
...
Products = owner.Products.Where(...).Select(product => new Product
{
Id = product.Id,
...
})
.ToList(),
})
.FirstOrDefault();