【问题标题】:EF Core "Group By could not be translated and will be evaluated locally."EF Core“无法翻译分组依据,将在本地进行评估。”
【发布时间】:2019-09-05 13:50:36
【问题描述】:

我正在使用 Entity Framework Core 2.2.6 对 Sql Server 数据库运行一个简单查询,但是 GroupBy 并未在服务器上执行,而是在本地执行。

有什么我遗漏的东西会迫使该组进入服务器吗?

我尝试过的 EF 查询的 2 种变体:

public class Holiday
{
    public int Id {get;set;}
    public DateTime Date {get;set;}
    public string Username {get;set;}
    public string Approver {get;set;}
}

//version 1
await _dbContext.Holidays
    .GroupBy(h => new { h.Date})
    .ToDictionaryAsync(x => x.Key.Date, x => x.Select(x1 => x1.Username).ToList());

//version 2
await _dbContext.Holidays
    .GroupBy(h => h.Date)
    .ToDictionaryAsync(x => x.Key, x => x.Select(x1 => x1.Username).ToList());

两种变体都会产生以下 SQL:

SELECT [h].[Id], [h].[Approver], [h].[Date], [h].[HolidayTypeId], [h].[OwningRequestId], [h].[HolidayStatusId], [h].[Username]
FROM [Holidays] AS [h]
ORDER BY [h].[Date]

警告产生:

警告:Microsoft.EntityFrameworkCore.Query[20500] 无法翻译 LINQ 表达式“GroupBy([h].Date, [h])”,将在本地进行计算。

来自cmets的建议:

//group by string
await _dbContext.Holidays
     .GroupBy(h => h.Username)
     .ToDictionaryAsync(x => x.Key, x => x.Select(x1 => x1.Username).ToList());

//group by part of date
await _dbContext.Holidays
     .GroupBy(h => h.Date.Year)
     .ToDictionaryAsync(x => x.Key, x => x.Select(x1 => x1.Username).ToList());
--group by string
SELECT [h].[Id], [h].[Approver], [h].[Date], [h].[HolidayTypeId], [h].[OwningRequestId], [h].[HolidayStatusId], [h].[Username]
FROM [Holidays] AS [h]
ORDER BY [h].[Username]

--group by part of date
SELECT [h].[Id], [h].[Approver], [h].[Date], [h].[HolidayTypeId], [h].[OwningRequestId], [h].[HolidayStatusId], [h].[Username]
FROM [Holidays] AS [h]
ORDER BY DATEPART(year, [h].[Date])

【问题讨论】:

  • 如果有,请显示 Holiday 类和映射
  • 如果我错了,有人可以纠正我,但我认为EF在后端生成SQL查询时不支持GroupBy
  • @Dortimer 从 Ef core 2.1 开始支持:docs.microsoft.com/en-us/ef/core/what-is-new/…
  • @miechoy 添加了假期班
  • @cmpbedes 这仍然只是一个猜测,但它如何处理除日期之外的分组?有时.Net 类型和SQL 类型不能很好地配合,这可能是因为无法根据数据库中的数据类型评估DateTime

标签: c# ef-core-2.2


【解决方案1】:

因为没有这样的 SQL 查询。

像 SQL 一样思考。如果您想按日期组获取用户名,则两者都需要。

基本上:

await _dbContext.Holidays
    .GroupBy(h => new { h.Date, h.Username})
    .Select(g => new
        {
          g.Key.Date,
          g.Key.Username
        });

这将产生一个这样的 SQL 查询。

SELECT [h].[Date],[h].[Username]
FROM [Holidays] AS [h]
GROUP BY [h].[Date],[h].[Username]

之后,您可以根据需要使用数据创建字典结构。

【讨论】:

    【解决方案2】:

    问题在于,当您尝试在数据库中进行分组时,您实际上没有办法在组内实现值。您只能访问SELECT 分组列或非分组列的聚合值(通过SUM 等)。

    例如:

    SELECT [h].[Date], [h].[Username]
    FROM [Holidays] AS [h]
    

    此查询将生成结果集,其中每一行将有两列,日期和名称。

    让我们尝试分组:

    SELECT [h].[Date], [h].[Username]
    FROM [Holidays] AS [h]
    GROUP BY [h.Date]
    

    根本不会评估此 SQL 查询,因为从 SQL 服务器的角度来看它是无效的。错误消息是

    Column 'Holidays.Username' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
    

    总结所有这些,您可以按照@Ercan Tirman 的建议进行操作,或者加载所有用户名和日期并将它们分组到内存中:

    var dateAndUsername = await _dbContext.Holidays
        .Select(x => new {x.Date, x.Username})
        .ToArrayAsync();
    
    Dictionary<DateTime, List<string>> grouped = dateAndUsername
        .GroupBy(x => x.Date)
        .ToDictionary(x => x.Key, x => x.Select(y => y.Username).ToList());
    

    【讨论】:

    • 我看到了我尝试做的错误。我会同意你的记忆分组建议谢谢你的解释!
    猜你喜欢
    • 2017-12-27
    • 2020-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多