为啥此 LINQ 表达式返回只有一项的子列表

Posted

技术标签:

【中文标题】为啥此 LINQ 表达式返回只有一项的子列表【英文标题】:Why this LINQ expressoin returns the sublist with only one item为什么此 LINQ 表达式返回只有一项的子列表 【发布时间】:2020-03-10 02:52:30 【问题描述】:

这是我的对象模型。

public class ItemViewModel

    public Item ItemObject  get; set; 
    public IEnumerable<LineItem> Fields  get; set;  = new List<LineItem>();


public class Item

    public int ItemId  get; set; 
    public DateTime DatePurchased  get; set; 


public class LineItem

    public int ItemId  get; set; 
    public string Name  get; set; 

数据库包含ItemsLineItems表,对应对象模型。我正在使用下面编写的 LINQ 表达式来检索 ItemViewModel 对象,并提供了一个项目 ID。

var i = from i in _dbContext.Items
        where i.ItemId == 123
        select new ItemViewModel
        
            ItemObject = i,
            Fields = from field in _dbContext.LineItems
                     where field.ItemId == i.ItemId
                     select field
        ;            
var viewModel = await i.SingleOrDefaultAsync();

代码编译良好,但在执行后viewModel.Fields 属性仅包含一个列表项,而在数据库表中针对 ItemID:123 有三个 Line Items。

此 sn-p 生成的 SQL(通过 SQL Profiler 捕获)通过 LEFT JOIN 返回三个行项目。这意味着生成的 SQL 很好,但 Entity Framework 的某些方面不太好,但我不知道是什么?

更新: 这是生成的 SQL 查询,它正确地给了我三行:

SELECT 
    [t].[ItemId], [t].[DatePurchased], 
    [s0].[ItemId], [s0].[Name]
FROM 
    (
    SELECT 
        TOP(2) [s].[ItemId], [s].[DatePurchased]
    FROM 
        [Items] AS [s]
    WHERE 
        [s].[ItemId] = 123
    ) AS [t]
LEFT JOIN [LineItems] AS [s0] ON [t].[ItemId] = [s0].[ItemId]
ORDER BY [t].[ItemId]

技术栈是 .NET Core 3.1 和 Entity Framework Core。

【问题讨论】:

你能提供这个生成的对应SQL吗? @John 是的,我可以,但这会使这个问题变得不必要地冗长。你认为生成的 SQL 对你有帮助吗? 我不能肯定,但它可能会。 LineItems keyless 实体类型吗?如果是,这是我第二次听到这个消息,我可以确认这是 EFC 错误。此外,它不仅返回每个父母 1 个孩子,而且还返回一个相同的(可能是第一个)记录,对吗?我在 EF Core 问题跟踪器中找不到错误,请考虑在此处发布问题 - github.com/dotnet/efcore/issues。 @IvanStoev 错误提交:github.com/dotnet/efcore/issues/20247 【参考方案1】:

正如 Ivan Stoev 在该问题下的 cmets 中所建议的那样,原因是 Entity Framework Core 中的一个错误,在将无密钥实体作为子实体处理时。我通过将主键添加到我之前的无键表中,然后验证相同的 LINQ 语句,对它进行了第一手测试。添加主键后,它开始给我一个包含 3 个项目的列表,而不是只有 1 个。所以在这个问题和我的测试之后,我已将其报告给 EF Core issue tracker。

对于在寻找答案时偶然发现此问题的任何人,我们有两种选择。向无键表添加主键或在 EF 实体模型中定义复合主键。我使用以下行修复了我的情况:

modelBuilder.Entity<LineItem>().HasKey(i => new  i.ItemId, i.Name );

之所以有效,是因为 LineItem.ItemIdLineItem.Name 的组合对于我的实体来说是独一无二的。这个答案的功劳归于an answer(虽然不是官方接受的答案)到较早的SO问题。

【讨论】:

以上是关于为啥此 LINQ 表达式返回只有一项的子列表的主要内容,如果未能解决你的问题,请参考以下文章

LINQ 表达式返回属性值?

为啥匹配的子字符串在 JavaScript 中返回“未定义”?

为啥没有 Linq 方法通过谓词返回不同的值?

LINQ之let关键字

具有匿名类型的 C# LINQ 构建表达式

使用 Linq 除了不像我想的那样工作