【问题标题】:GroupBy Expression failed to translateGroupBy 表达式无法翻译
【发布时间】:2020-01-07 17:57:40
【问题描述】:
//Model
public class Application
{
    [Key]
    public int ApplicationId { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime ConfirmedDate { get; set; }
    public DateTime IssuedDate { get; set; }
    public int? AddedByUserId { get; set; }
    public virtual User AddedByUser { get; set; }
    public int? UpdatedByUserId { get; set; }
    public virtual User UpdatedByuser { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public string TRN { get; set; }
    public string EmailAddress { get; set; }
    public string Address { get; set; }
    public int ParishId { get; set; }
    public Parish Parish { get; set; }
    public int? BranchIssuedId { get; set; }
    public BranchLocation BranchIssued { get; set; }
    public int? BranchReceivedId { get; set; }
    public BranchLocation BranchReceived {get; set; }
}

public async Task<List<Application>> GetApplicationsByNameAsync(string name)
{
    if (string.IsNullOrEmpty(name))
        return null;
    return await _context.Application
        .AsNoTracking()
        .Include(app => app.BranchIssued)
        .Include(app => app.BranchReceived)
        .Include(app => app.Parish)
        .Where(app => app.LastName.ToLower().Contains(name.ToLower()) || app.FirstName.ToLower()
        .Contains(name.ToLower()))
        .GroupBy(app => new { app.TRN, app })
        .Select(x => x.Key.app)
        .ToListAsync()
        .ConfigureAwait(false);
}

上述GroupBy 表达式在VS Studio 中编译失败。我的目标是按包含用户给定字符串的名称运行查询过滤结果,然后它应该按类似的TRN 数字对结果进行分组,返回这些应用程序的列表以返回视图。我想我真的很接近,但似乎无法弄清楚查询的最后一点。任何指导表示赞赏。

出现错误

InvalidOperationException: The LINQ expression 'DbSet<Application>
.Where(a => a.LastName.ToLower().Contains(__ToLower_0) || a.FirstName.ToLower().Contains(__ToLower_0))
.GroupBy(
source: a => new {
TRN = a.TRN,
app = a
},
keySelector: a => a)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()

更新 似乎这肯定是由于最近更新以来 .net core 3.x 和 EF core 如何协同工作发生了变化。我不得不使用AsEnumerable() 而不是ToListAsync() 将其更改为客户评估。 Steve py 给出的其余查询使用此方法。即使在阅读文档之后,我也不知道 groupby 在 LINQ 中是如何真正工作的,所以这对我有很大帮助。但是,将查询带到客户端 eval 可能会出现性能问题。

【问题讨论】:

  • 编译时或运行时错误?突出显示哪一行?也许将.GroupBy(app =&gt; new { app.TRN, app }) 更改为.GroupBy(app =&gt; app.TRN)
  • @JohnyL 我将错误添加到问题中作为编辑。我确实尝试过,但我需要查询的输出格式为List&lt;Application&gt;
  • .GroupBy(app =&gt; new { app.TRN, app }) 更改为.GroupBy(app =&gt; app.TRN) 有帮助吗?
  • 不,不幸的是,它仍然没有给我正确的结果集。嗯,也许我应该以不同的方式解决这个问题。我想按 TRN 分组,这是一组重复的数字,例如 12345,在 Application 表中可能有许多具有相同序列的记录,我只想要每组 TRN 序列中的最新行。因此,如果该表有 3 行带有该 TRN,我只希望返回最后插入的行。这就是我试图为每组不同的 TRN 数字实现的目标
  • 我回滚了您的上一个修订版,因为它会将问题更改为一个新问题,它不尊重现有答案。

标签: entity-framework entity-framework-6 ef-core-3.0 .net-core-3.1


【解决方案1】:

基于此:

我想按 TRN 进行分组,这是一组重复的数字,例如 12345,在 Application 表中可能有许多具有相同序列的记录,我只想要每组 TRN 序列中的最新行。

我相信这应该可以满足您的需求:

return await _context.Application
    .AsNoTracking()
    .Include(app => app.BranchIssued)
    .Include(app => app.BranchReceived)
    .Include(app => app.Parish)
    .Where(app => app.LastName.ToLower().Contains(name.ToLower()) || app.FirstName.ToLower()
    .Contains(name.ToLower()))
    .GroupBy(app => app.TRN)
    .Select(x => x.OrderByDescending(y => y.CreatedAt).First())
    .ToListAsync()
    .ConfigureAwait(false);

GroupBy 表达式应该代表您想要分组的内容。在您的情况下,TRN。当我们从那里进行选择时,x 代表每个“组”,其中包含属于每个 TRN 的 Enumarable 应用程序集。因此,我们按 CreatedAt 日期的降序对它们进行排序,以使用 First 选择最新的。

试一试。如果这不是您所追求的,请考虑为您的问题添加一个示例集以及所需的输出与此处产生的输出/错误。

【讨论】:

  • 实际上你已经帮助我解释了 groupby 多一点,我并没有考虑这些组是真正的“组”项目。我会试一试并报告结果
  • could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly 这是我在尝试运行查询时遇到的错误的 sn-p,正如您所概述的那样。我正在使用.net core 3.0,目前不确定是否与它有关。
  • 该错误表明表达式中存在 EF Core 无法转换为 SQL 的内容。我认为这可能是 EF Core 3 中的一个重大变化,因为我已经在 EF Core 2.6 和 EF 6 中测试了类似的表达式并且它们翻译得很好。如果您将 SQL Server 作为数据库运行,则不需要额外的 ToLower() 用于字符串比较,前提是使用默认的不区分大小写的排序规则。我尝试将我的测试项目从 2.6 更新到 3.0,但由于模型构建器中的一些重大变化,它对我的​​实体配置产生了极大的影响。 EF Core 团队是一个功能失调的 IMO ...
  • 你可以试试.Where(app =&gt; app.LastName.Contains(name) || app.FirstName.Contains(name))。我知道这不是一个理想的解决方案,因为它会对排序规则做出假设,但它可能会确认或否认问题所在。
  • 意在补充,使用 Core 2.6 生成的原始 ToLower 语句导致:WHERE (CHARINDEX(@__ToLower_0, LOWER([x].[Name])) &gt; 0) OR (@__ToLower_0 = N'''')', 没有客户评估。因此,这可能是 Core 3 的一个怪癖,或者以某种方式特定于您的示例。
【解决方案2】:

我遇到了一个类似的问题,我觉得它既有趣又愚蠢。似乎 EF 团队禁止在 GROUP BY 之前执行 WHERE,因此它不起作用。我不明白你为什么不能这样做,但这似乎是迫使我实现程序而不是很好地构建代码的方式。

LMK,如果你找到办法。

注意:只有当您第一次分组然后执行位置时,它们才具有分组依据(完整表格的分组元素的位置 => 对我来说没有任何意义)

【讨论】:

  • 或者(如果你能忍受的话),在你的过滤器之后选择所有内存并将其分组到内存中。我不喜欢将所有数据从 SQL 获取到客户端,因为它的数据流过多并且会减慢一切。
【解决方案3】:

EF 核心中的 GroupBy 支持是个笑话。

这在 EF6 的服务器上完美运行

var nonUniqueGroups2 = db.Transactions.GroupBy(e => new { e.AccountId, e.OpeningDate })
    .Where(grp => grp.Count() > 1).ToList();

在 EF 核心中,它会导致异常“无法翻译给定的 'GroupBy' 模式。在 'GroupBy' 之前调用 'AsEnumerable' 以在客户端对其进行评估。”该消息具有误导性,请勿致电AsEnumerable,因为这应该在服务器上处理。

我找到了解决方法here。额外的Select 会有所帮助。

                var nonUniqueGroups = db.Transactions.GroupBy(e => new { e.AccountId, e.OpeningDate })
                    .Select(x => new { Key = x.Key, Count = x.Count() })
                    .Where(x => x.Count > 1)
                    .ToList();

解决方法的缺点是结果集不包含组中的项目。

有一个 EF Core issue。请投票,以便他们真正解决这个问题。

【讨论】:

    猜你喜欢
    • 2021-12-28
    • 1970-01-01
    • 2022-12-03
    • 2021-06-26
    • 2022-10-20
    • 1970-01-01
    • 2021-11-27
    • 1970-01-01
    相关资源
    最近更新 更多