为什么这个 LINQ 表达式 [=第 10 个子列表只有一个项目
Why this LINQ expressoin returns the sublist with only one item
这是我的对象模型。
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; }
}
数据库包含Items和LineItemstables,对应对象模型。我正在使用下面编写的 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
属性 仅包含一个列表项,而数据库 table 中有三个针对 ItemID:123 的行项.
此代码段生成的 SQL(通过 SQL Profiler 捕获)returns 三个行项目,通过 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。
正如 Ivan Stoev 在此问题下的评论中所建议的那样,原因是 Entity Framework 核心中的错误,将无键实体作为子实体处理时。我通过向我之前的无密钥 table 添加一个主键,然后验证相同的 LINQ 语句,对其进行了第一手测试。添加主键后,它开始给我一个包含 3 个项目的列表,而不仅仅是 1 个。所以在这个问题和我的测试之后,我已经将它报告给 EF Core issue tracker。
对于任何在寻找答案时偶然发现这个问题的人,我们这里有两个选择。将主键添加到无键 table 或在 EF 实体模型中定义复合主键。我使用以下行修复了我的情况:
modelBuilder.Entity<LineItem>().HasKey(i => new { i.ItemId, i.Name });
之所以有效,是因为 LineItem.ItemId
和 LineItem.Name
组合对于我的实体来说是独一无二的。此答案归功于 an answer(尽管未被正式接受的答案)对较早的 SO 问题。
这是我的对象模型。
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; }
}
数据库包含Items和LineItemstables,对应对象模型。我正在使用下面编写的 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
属性 仅包含一个列表项,而数据库 table 中有三个针对 ItemID:123 的行项.
此代码段生成的 SQL(通过 SQL Profiler 捕获)returns 三个行项目,通过 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。
正如 Ivan Stoev 在此问题下的评论中所建议的那样,原因是 Entity Framework 核心中的错误,将无键实体作为子实体处理时。我通过向我之前的无密钥 table 添加一个主键,然后验证相同的 LINQ 语句,对其进行了第一手测试。添加主键后,它开始给我一个包含 3 个项目的列表,而不仅仅是 1 个。所以在这个问题和我的测试之后,我已经将它报告给 EF Core issue tracker。
对于任何在寻找答案时偶然发现这个问题的人,我们这里有两个选择。将主键添加到无键 table 或在 EF 实体模型中定义复合主键。我使用以下行修复了我的情况:
modelBuilder.Entity<LineItem>().HasKey(i => new { i.ItemId, i.Name });
之所以有效,是因为 LineItem.ItemId
和 LineItem.Name
组合对于我的实体来说是独一无二的。此答案归功于 an answer(尽管未被正式接受的答案)对较早的 SO 问题。