繁体   English   中英

包含路径表达式必须引用在类型上定义的导航属性(使用 LINQ 选择数据)

[英]The Include path expression must refer to a navigation property defined on the type (select data using LINQ)

我试图使用 LINQ 获取 select 数据,我有一个名为“产品”的列表,我只想要产品列表中存在的这些项目

var Owner = db.Owners
  .Where(m => m.ID == id)
  .Include(m => m.Products.Where(item1 => products.Any(item2 => item2.ProductID == item1.ProductID)).ToList())
  .FirstOrDefault();

但我收到此错误:

System.ArgumentException: '包含路径表达式必须引用在类型上定义的导航属性。 对参考导航属性使用虚线路径,对集合导航属性使用 Select 运算符。 参数名称:路径'

Include 旨在获取表的完整行,包括主键和外键。

通常,获取表的完整行效率不高。 例如,假设您有一个包含学校和学生的数据库。 Schools和Student之间存在一对多的关系:每个School有零个或多个Student,每个Student恰好就读一所School,即外键所指的School。

如果您获取包含 2000 个学生的 School [10],则每个 Student 都将有一个值为 10 的外键 SchoolId。如果您使用 Include 并获取完整的 Student 行,您将传输该值 10 超过 2000 次。 多么浪费处理能力。

DbContext 有一个 ChangeTracker object。 每当您在不使用 Select 的情况下获取数据时,因此如果您获取完整的行,那么获取的行将与它的克隆一起存储在 ChangeTracker 中。 您会得到对克隆的引用(或原始的,没关系)。 当您更改获取数据的属性时,您会更改 Clone 中的值。 当您调用SaveChanges时,ChangeTracker 中所有原始文件的所有属性的值都与 Clone 中的值进行比较。 更改的项目在数据库中更新。

因此,如果您获取 School [10] 及其学生,您不仅会获取比您将使用的更多的数据,而且您还会将所有这些学生与克隆学生一起存储在 ChangeTracker 中。 如果您为完全不同的东西(例如学校的电话号码)调用 SaveChanges,那么所有学生都将与他们的克隆进行逐个属性的值比较。

每当您使用 Entity Framework 获取数据时,请始终使用Select和 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();

这样就无法将您的查询拆分为 2 个查询。

var owner = db.Owners
    .Include(m => m.Products)
    .FirstOrDefault(m => m.ID == id);
if(owner != null)
{
var ownerProducts = owner.Products
  .Where(item1 => products.Any(item2 => item2.ProductID == item1.ProductID)).ToList();
}

也许这样就可以了。

尝试以下操作:

var productIds = products.Select(x => x.ProductID);

var Owner = db.Owners
  .Where(m => m.ID == id)
  .Include(m => m.Products.Where(product => productIds.Contains(product.ProductID))
  .FirstOrDefault();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM