【发布时间】:2018-12-12 15:48:12
【问题描述】:
我正在尝试列出所有项目,其中包含一个额外的列来描述它是否由当前用户拥有。
所以我正在寻找生成类似于以下 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
...
}
}
所以我实际上可以实现整个项目,我只需要显式键入每个最后一个属性。好玩!
【问题讨论】:
-
In my project this code results in an unfiltered SELECT statement querying every Item, then for each of them a separate query to check if it exists in the OwnedItems table.-- 这也是您想要的查询所做的。我们可以看到查询 EF Core 2.1 的输出吗? -
不完全一样。 LinqPad 在一次访问 sql 服务器的过程中运行它大约需要 0.2 秒。在我的项目中,使用数百个查询运行相同的查询需要几秒钟。我从我的项目 SQL 查询中添加了一些代码。
-
啊,在你更新了你的问题之后,你的意思就更清楚了。我相信发布的答案会帮助您解决这个问题
标签: c# sql entity-framework linq linqpad