为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
慕码人2483693 2022-06-12 10:32:57
我试图用一个额外的列列出所有项目,描述它是否由当前用户拥有。所以我正在寻找一个生成类似于以下 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 OwnedFROM Items;根据互联网以及成功的 LinqPad 实验,此代码应该可以工作。from item in Itemsselect 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]))笔记:上面的代码是手动修改的,所以可能有错别字。请无视他们。我知道这个特定的查询可以使用左连接和检查空值来完成,但我的实际查询更复杂,我需要(嵌套)存在子句。
查看完整描述

2 回答

?
喵喔喔

TA贡献1735条经验 获得超5个赞

您可能需要为查询中的“OwnedItems”导航属性启用即时加载: https ://docs.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


查看完整回答
反对 回复 2022-06-12
?
万千封印

TA贡献1891条经验 获得超3个赞

您需要告诉 EF 加载相关数据,在本例中为OwnedItems表。

解决此问题的一种方法是包含相关表。如果有链接表的外键,则可以像这样轻松完成:

var dataWithRelatedData = db_context.Items.Include(x => x.OwnedItems).Select ...

避免大量往返数据库的另一种方法是将两个数据集加载到单独的查询中,然后将它们合并到内存中。因此,您将首先对Items进行查询,然后将数据返回到OwnedItems的另一个查询,最后将它们合并到一个对象列表中。这只会对数据库进行 2 次调用,从而提高性能。


查看完整回答
反对 回复 2022-06-12
  • 2 回答
  • 0 关注
  • 134 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号