繁体   English   中英

EF Core 2.2 LINQ 查询在 EF Core 3.0 中不起作用

[英]EF Core 2.2 LINQ query not working in EF Core 3.0

下面的代码在 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();

我得到的错误:

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

请需要帮助

这是由于EF Core 3.0中的一项重大更改,即: 不再在客户端上评估 LINQ 查询

因此,以 EF Core 可以将表达式转换为 T-SQL 或将数据提取到 memory 的方式编写查询,然后进行查询。

原始查询存在问题,但 EF Core 将其隐藏在地毯下,从而减慢了一切速度。

当它在 LINQ 到 SQL 中引入并在实体框架中删除时,客户端评估是邪恶的。 我想不出为什么人们虽然将它添加回 EF Core 是个好主意,但现在它已经消失了,这是一件好事。 原始查询也不会在 EF 6.2 中运行。

原始查询需要一些修复,这可能会提高性能。 首先,从关系和导航属性生成连接是 ORM 的工作。

其次,即使在 SQL 中,也不可能在 SELECT 子句中添加不属于GROUP BY或聚合的字段。 除非使用窗口 function,否则没有与FirstOrDefault()等效的聚合 function。

要获取 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();

如果实体被修改,例如 Asset 具有 Category 属性,则查询可以简化为:

 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 查询。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM