EF Core 2.2 中的LINQ查询在 EF Core 3.0 中无法工作

34

以下代码在 EF Core 2.2 上运行良好,但在 EF Core 3.0 上无法正常工作。

 var items = (from asset in Context.Assets
              join assetCategory in Context.AssetCategories on asset.CategoryId equals assetCategory.Id
              group assetCategory by assetCategory.Id into assetCategories
              select new AssetCategorySummary
              {
                  CategoryId = assetCategories.Key,
                  CategoryName = assetCategories.Select(p => p.CategoryName).FirstOrDefault(),
                  TotalAsset = assetCategories.Count()
              }).ToListAsync();

我收到的错误:

'NavigationExpandingExpressionVisitor' 无法处理 LINQ 表达式 'AsQueryable(Select<AssetCategory, string>( source: NavigationTreeExpression Value: default(IGrouping<Guid, AssetCategory>) Expression: (Unhandled parameter: e), selector: (p) => p.CategoryName))'。这可能表明 EF Core 中存在一个错误或限制。 有关更详细的信息,请参见 https://go.microsoft.com/fwlink/?linkid=2101433

请求帮助。


2
这不是一个问题。升级到EF Core 3时可能会有变化。除非与此相关的确切问题,否则无需在此处报告它们。 - Gert Arnold
1
@S. Aziz Kazdal 是的!这就是我告诉你的“将数据提取到内存中,然后进行查询”,尽管这不是很有效,因为这会影响性能或在处理大量数据时失败。 - TanvirArjel
@S.AzizKazdal 您可以编写原始的SQL语句并使用FromSql方法。更多详细信息请参见:https://entityframeworkcore.com/querying-data-raw-sql-queries - TanvirArjel
@TanvirArjel 不是 FromSql,这不是我想要的。我想执行这个查询并将值获取到列表中。 select Kategori.Id,Kategori.CategoryName,count(Varlik.Id) as [Varlık Sayısı] from Assets Varlik inner join AssetCategories Kategori on Kategori.Id = Varlik.CategoryId group by Kategori.Id,Kategori.CategoryName - S. Aziz Kazdal
在您进行修改后,我的意思是 GroupBy(p => new { p.Id, p.CategoryName }) - Gert Arnold
显示剩余3条评论
3个回答

16

2
自动客户端评估已经完全消失了 - 它不是一个选项,也不能被启用。 - Ivan Stoev
1
是的,请查看第二个链接中的缓解措施部分 - 如果查询无法完全翻译,则重写查询以便能够翻译,或使用AsEnumerable()、ToList()或类似方法显式地将数据带回客户端,然后可以使用LINQ-to-Objects进一步处理数据。 如您所见,没有启用它的选项。此外,QueryClientEvaluationWarning被标记为[Obsolete("Automatic client evaluation is no longer supported. This event is no longer generated.")] - Ivan Stoev
4
我不理解这个重大更改。即使是之前在ef 2.x版本中可以正常工作的简单分组操作现在也失败了。 - jjxtra

16

原始查询存在问题,但EF Core把它隐藏了起来,导致所有操作都变慢。

当LINQ to SQL引入客户端评估时,它是有害的,并在Entity Framework中被删除。我想不出为什么人们认为将其添加回EF Core是个好主意,但现在它已经消失了是件好事。原始查询也无法在EF 6.2中运行。

首先,ORM负责从关系和导航属性生成连接。其次,即使在SQL中,也无法添加一个在SELECT子句中不属于GROUP BY或聚合的字段。除非使用窗口函数,否则没有聚合函数等同于FirstOrDefault()

要在SQL中获取类别名称,我们必须将其包含在GROUP BY中,或者使用CTE /子查询按ID分组,然后查找类别名称,例如:

SELECT CategoryID,CategoryName,Count(*)
FROM Assets inner join AssetCategories on CategoryID=AssetCategories.ID
GROUP BY CategoryID,CategoryName
或者
SELECT CategoryID,CategoryName,Cnt
FROM (select CategoryID, Count(*) as Cnt
      from Assets
      group by CategoryID) a 
INNER JOIN AssetCategories on CategoryID=AssetCategories.ID

在 LINQ 中与第一个查询相当的是:

 var items = (from asset in Context.Assets
              join assetCategory in Context.AssetCategories on asset.CategoryId equals assetCategory.Id
              group asset by new {assetCategory.Id,assetCategory.CategoryName} into summary
              select new AssetCategorySummary
              {
                  CategoryId   = summary.Key.Id,
                  CategoryName = summary.Key.Name,
                  TotalAsset   = summary.Count()
              }).ToListAsync();
如果实体被修改,例如资产具有类别属性,则查询可以减少为:

 var items = (from asset in Context.Assets
              group asset by new {asset.Category.Id,asset.Category.CategoryName} into summary
              select new AssetCategorySummary
              {
                  CategoryId   = summary.Key.Id,
                  CategoryName = summary.Key.Name,
                  TotalAsset   = summary.Count()
              }).ToListAsync();

这需要一些测试来确保它创建了一个合理的查询。过去曾经有一些令人惊讶的情况,而我还没有时间检查最终 EF Core 3.0 生成的 SQL。

更新

LINQPad 6 可以使用 EF Core 3,甚至可以使用外键约束从数据库生成 DbContext。

此查询

 var items = (from asset in Context.Assets
              group asset by new {asset.Category.Id,asset.Category.CategoryName} into summary
              select new AssetCategorySummary
              {
                  CategoryId   = summary.Key.Id,
                  CategoryName = summary.Key.Name,
                  TotalAsset   = summary.Count()
              }).ToListAsync();

生成一个漂亮的SQL查询:

SELECT [a0].[ID] AS [CategoryId], [a0].[CategoryName], COUNT(*) AS [TotalAsset]
FROM [Assets] AS [a]
INNER JOIN [AssetCategories] AS [a0] ON [a].[CategoryID] = [a0].[ID]
GROUP BY [a0].[ID], [a0].[CategoryName]

使用join会生成相同的SQL查询。


6

你仍然可以通过客户端评估执行任何类型的集合操作,只需在执行集合操作前插入AsEnumerable()即可。这是所有集合操作在3.0版本之前的处理方式,根据确切的用例,客户端评估可能与服务器评估一样有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接