[英]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.