【问题标题】:EF Core 2.1 RC 1 GroupBy still evaluates locallyEF Core 2.1 RC 1 GroupBy 仍在本地评估
【发布时间】:2019-04-23 17:37:06
【问题描述】:

我按照 ASP.NET 博客中的 official guide 将 ASP.NET Core 项目从版本 2.0.x 升级到 2.1.0-rc1EF Core 包也更新到了相同的版本。我使用的数据库提供程序是Microsoft.EntityFrameworkCore.SqlServer。我期待着这次升级,因为我需要很多LINQ GroupBy translation 功能。我的查询之一如下:

await _db.Documents
         .ApplyFilter(options)
         .Where(x => x.SubscriptionId == subscriptionId && x.Status != DocumentStatus.Deleted)
         .GroupBy(document => new { document.SubscriptionId })
         .Select(group => new {
            Total = group.Sum(x => x.Total),
            TotalDiscount = group.Sum(x => x.TotalDiscount),
            TotalNet = group.Sum(x => x.TotalNet != null ? x.TotalNet.Value * (decimal)x.CurrencyRate : 0),
            SubTotal = group.Sum(x => x.SubTotal != null ? x.SubTotal.Value * (decimal)x.CurrencyRate : 0),
            TotalSalesTax = group.Sum(x => x.TotalSalesTax != null ? x.TotalSalesTax.Value * (decimal)x.CurrencyRate : 0),
            TotalTax = group.Sum(x => x.TotalTax != null ? x.TotalTax.Value * (decimal)x.CurrencyRate : 0),
            TotalPayable = group.Sum(x => x.TotalPayable != null ? x.TotalPayable.Value * (decimal)x.CurrencyRate : 0)
        })
        .ToListAsync();

当这个查询运行时,我仍然会收到一条警告:

EFCore Linq 查询无法翻译,将在本地进行评估。

所以我想问一下我是否做错了什么(非常)或者这个功能还没有像宣传的那样可用?你们中有人尝试过类似的东西吗?提前感谢您的帮助。


编辑

为了测试问题,尝试了以下查询(从 Sum 方法中删除了条件检查),正如 cmets 中所建议的 Ivan Stoev,但结果是相同的。

await _db.Documents
         .ApplyFilter(options)
         .Where(x => x.SubscriptionId == subscriptionId && x.Status != DocumentStatus.Deleted)
         .GroupBy(document => new { document.SubscriptionId })
         .Select(group => new {
            Total = group.Sum(x => x.Total),
            TotalDiscount = group.Sum(x => x.TotalDiscount),
            TotalNet = group.Sum(x => x.TotalNet),
            SubTotal = group.Sum(x => x.SubTotal),
            TotalSalesTax = group.Sum(x => x.TotalSalesTax),
            TotalTax = group.Sum(x => x.TotalTax),
            TotalPayable = group.Sum(x => x.TotalPayable)
          })
          .ToListAsync();

解决方案

经过实验,我发现了以下内容。我发布它以防有人遇到同样的问题。以下查询完美地转换为 T-SQL 语句,因此不在客户端上进行评估。

await (from d in _db.Documents.ApplyFilter(options)
       where d.SubscriptionId == subscriptionId && d.Status != DocumentStatus.Deleted
       group d by d.SubscriptionId into g
       select new {
          Total = g.Sum(x => x.Total/* * Convert.ToDecimal(x.CurrencyRate)*/),
          TotalDiscount = g.Sum(x => x.TotalDiscount/* * Convert.ToDecimal(x.CurrencyRate)*/),
          TotalNet = g.Sum(x => x.TotalNet/* * Convert.ToDecimal(x.CurrencyRate)*/),
          SubTotal = g.Sum(x => x.SubTotal/* * Convert.ToDecimal(x.CurrencyRate)*/),
          TotalSalesTax = g.Sum(x => x.TotalSalesTax/* * Convert.ToDecimal(x.CurrencyRate)*/),
          TotalTax = g.Sum(x => x.TotalTax/* * Convert.ToDecimal(x.CurrencyRate)*/),
          TotalPayable = g.Sum(x => x.TotalPayable/* * Convert.ToDecimal(x.CurrencyRate)*/)
       }).ToListAsync();

明显的区别是我使用 LINQ 查询语法 而不是 扩展方法。此外,我将 CurrencyRate 属性的乘法注释为 它也会导致在本地评估查询。这似乎很奇怪,因为查询语法也可以转换为引擎盖下的扩展方法。

我还在 Entity Framework Core 的 Github 存储库中打开了一个问题 你可以找到here

【问题讨论】:

  • 你甚至没有告诉你正在使用哪个提供商
  • 您能否向我们展示一个不会生成该警告的类似查询(没有GroupBy),以便我们比较两者?
  • @Tseng 我编辑了帖子。我正在使用Microsoft.EntityFrameworkCore.SqlServer(所以我使用的是 Sql Server)。
  • 离题,x.TotalNet != null ? x.TotalNet.Value * (decimal)x.CurrencyRate : 0 可以缩短为(x.TotalNet ?? 0) * (decimal)x.CurrencyRate。我也不确定(decimal) 在这里是否有用。这也可能涉及 EF Core 无法翻译您的 linq 查询但我尚未确认的原因。
  • @mjwills 如here 所述,有些查询在服务器上评估,而其他查询在客户端评估。 GroupBy 是一种扩展方法,不能被翻译成 T-SQL 语句,因此它在服务器上运行。这应该在 EF Core 2.1 中修复为announced,但我真的很难看到它在起作用。所以,我的其他查询按预期工作。我只是试图找出我的错误中的这种特定情况还是尚未实施。

标签: c# .net-core entity-framework-core


【解决方案1】:

在我的例子中,这个查询的分组是在客户端评估的:

await context.MyCollection
.GroupBy(x => x.Tag.Id)
.ToDictionaryAsync(x => x.Key, x => x.Count());

将其更改为以下使其在服务器端进行评估:

await context.MyCollection
.GroupBy(x => x.Tag.Id)
.Select(g => new {g.Key, Count = g.Count()})
.ToDictionaryAsync(arg => arg.Key, arg => arg.Count);

【讨论】:

    【解决方案2】:

    GroupBy 是一个红鲱鱼。

    使用查询语法编译为使用扩展方法,所以这不是问题。

    我可以看到的编辑版本和查询语法版本之间的唯一区别是,在仍然在本地评估 group by 的情况下,您执行_db.Documents.ApplyFilter(...)。我猜这会导致返回类型转换为其他类型,并且选择了错误的重载,或者一些使查询翻译器感到困惑的类似细节——这将导致 all 的 LINQ 运算符( Where、Select、GroupBy 等)要在本地进行评估。

    【讨论】:

    • 很抱歉省略了这一点。 ApplyFilter 方法包含更多的Where 方法并返回一个IQueryable。我已经检查过是否添加它没有任何区别。
    猜你喜欢
    • 2017-12-27
    • 1970-01-01
    • 1970-01-01
    • 2020-03-14
    • 1970-01-01
    • 1970-01-01
    • 2020-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多