gpt4 book ai didi

c# - Entity Framework Core LINQ 麻烦创建(选择案例存在)查询

转载 作者:行者123 更新时间:2023-11-30 22:57:20 24 4
gpt4 key购买 nike

我试图用一个额外的列来列出所有项目,该列描述它是否为当前用户所有。

所以我正在寻找一个生成类似于以下 SQL 的 Linq 查询:

SELECT *,
CASE WHEN
EXISTS (
SELECT NULL FROM OwnedItems
WHERE OwnedItems.UserId = @UserId AND OwnedItems.ItemId = Items.Id
)
THEN 'true'
ELSE 'false'
END AS Owned
FROM Items;

根据互联网以及成功的 LinqPad 实验,此代码应该有效。

from item in Items
select new
{
Owned = OwnedItems.Any(own => own.UserId == userId && own.ItemId == item.Id),
Item = item
}

在 LinqPad 中,此代码生成与我想要的完全相同的 SQL。但在我的项目中,它做了完全不同的事情。

我的代码是一个使用 Entity Framework Core 2.1 的 .Net Core 2.1 项目。由于它是一个核心项目,我不能直接在 LinqPad 中测试它,因为它还不受支持。

在我的项目中,此代码生成一个未过滤的 SELECT 语句来查询每个项目,然后对每个项目进行单独的查询以检查它是否存在于 OwnedItems 表中。像这样:

运行此查询的 1 个实例:

Executed DbCommand (68ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT *
FROM [Items] AS [item]

接着是数百个这样的查询,需要几秒钟才能运行:

 Executed DbCommand (32ms) [Parameters=[@__userId_0='?' (DbType = Int32), @_outer_Id='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM [OwnedItems] AS [ownedItems]
WHERE ([ownedItems].[UserId] = @__userId_0) AND ([ownedItems].[ItemId] = @_outer_Id))
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END

一些进一步的信息,也许它有帮助:如果我使用同一行作为 where 子句的一部分,它会完美地工作。

var q = from item in Items
where OwnedItems.Any(o => o.UserId == userId && o.ItemId == item.Id)
select item;

上面的 linq 产生了这个漂亮的 sql:

SELECT *
FROM [Items] AS [item]
WHERE EXISTS (
SELECT 1
FROM [OwnedItems] AS [o]
WHERE ([o].[UserId] = @__userId_0) AND ([o].[ItemId] = [item].[Id]))

注意事项:

  • 上面的代码已被手动破坏,因此其中可能存在拼写错误。请忽略它们。

  • 我知道这个特定的查询可以使用左连接并检查空值来完成,但我的实际查询更复杂,我需要(嵌套的)exists 子句。

解决方案更新

正如@KorsG 所指出的,如果 Item 没有具体化,则会生成正确的查询。我发现即使我写了以下内容,也无法实现 Item 的工作:

from item in Items
select new
{
Owned = OwnedItems.Any(own => own.UserId == userId && own.ItemId == item.Id),
// Item = item //THIS LINE GENERATES BAD QUERY
Item = new Item {
Id = item.Id,
Name = item.Name,
...
[Literally every single property listed one by one] = item.CorrespondingProperty
...
}
}

所以我实际上可以具体化整个项目,我只需要显式键入每个最后的属性。有趣!

最佳答案

您可能需要为查询中的“OwnedItems”导航属性启用预先加载: https://learn.microsoft.com/en-us/ef/core/querying/related-data#eager-loading

如果我要举个例子,请发布完整的 linq 查询。

更新 1

似乎子查询在 EF Core 中有 N+1 问题,它可能会在版本 3 中修复。

引用:https://github.com/aspnet/EntityFrameworkCore/issues/10001

更新 2

如果您不需要完全具体化“项目”,您应该能够做这样的事情,您可以在其中创建一个匿名对象,而不是“欺骗”EF 成为您想要的东西:

from item in Items
select new
{
Owned = OwnedItems.Any(own => own.UserId == userId && own.ItemId == item.Id),
Item = new { Id = item.Id, Name = item.Name }
}

引用:https://github.com/aspnet/EntityFrameworkCore/issues/11186

关于c# - Entity Framework Core LINQ 麻烦创建(选择案例存在)查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53746633/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com