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

Posted

技术标签:

【中文标题】包含路径表达式必须引用在类型上定义的导航属性(使用 LINQ 选择数据)【英文标题】:The Include path expression must refer to a navigation property defined on the type (select data using LINQ) 【发布时间】:2022-01-03 03:57:37 【问题描述】:

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

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 运算符。 参数名称:路径'

【问题讨论】:

【参考方案1】:

尝试以下方法:

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();

【讨论】:

你,很像 OP,似乎认为Include 做了任何事情(在你的情况下,你似乎相信它会过滤?)。事实并非如此,Include,例如GC.KeepAlive(),只是为了引用一个字段而引用它。在 EF 的情况下,您使用 Include 来引用您想要选择的外部表以及查询的其余部分。这绝对不涉及过滤。 @Blindy,实际上没有,我在 LINQ to Entities 方面非常有经验。 OP 正在尝试过滤掉相关实体,这是最简单的方法。 我相信你确实很有经验,但是Include 不过滤任何东西 @Blindy,是的,我确定Filtered Include 您说的是 LINQ to Entities,而不是 EF.Core。【参考方案2】:

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();

【讨论】:

以上是关于包含路径表达式必须引用在类型上定义的导航属性(使用 LINQ 选择数据)的主要内容,如果未能解决你的问题,请参考以下文章

包含路径表达式必须引用 type.in 预加载中定义的导航属性

EF Lambda:包含路径表达式必须引用导航属性[重复]

如何在EF6.1中过滤掉子集合

EF 6 代码首先,在导航属性上使用包含更改外键 ID 会导致“发生引用完整性约束违规”错误

c语言中,定义数组和引用数组时,其数组的下标的数据类型分别允许是啥?

xpath语法