【问题标题】:ef-core nested table aggregate functions give "NavigationExpandingExpressionVisitor failed"ef-core 嵌套表聚合函数给出“NavigationExpandingExpressionVisitor failed”
【发布时间】:2020-05-01 06:18:27
【问题描述】:

我有一个嵌套数据模型,我想从中获取按顶级属性分组的聚合数据。 我的模型例如:

public class Scan {
    public long Id {get; set;}
    public int ProjectId { get; set; }
    public int ScanUserId { get; set; }
    public ICollection<ScanItem>? Items { get; set; }
}

public class ScanItem
{
    public long Id { get; set; }
    public long InventoryScanId { get; set; }
    public double Qty { get; set; }
}

我想获取按 Scan.ScanUserId 分组的所有扫描,然后获取 ScanItems.Qty 的总和,例如每个用户。我的查询看起来像这样,EF 给出了以下错误:

处理 LINQ 表达式 'AsQueryable((Unhandled 参数:x).Items)' by 'NavigationExpandingExpressionVisitor' 失败

from scan in Scans
        .Include(x=>x.ScanUser)
        .Include(x=>x.Items)
    group scan by new { scan.ScanUser.Name, scan.ScanUser.Id } into g
    select new
    {
        UserId = g.Key.Id,
        Name = g.Key.Name,
        LastSyncTime = g.Max(x => x.ScanDate),
        ScanItems = g.Sum(x=>x.Items.Sum(i=>i.Qty))
    }

如何在嵌套表的属性上运行聚合函数而不在客户端对其进行评估?

【问题讨论】:

  • 我认为Includes 是不必要的。另外,为什么Items 可以为空?您可以更改为:public ICollection&lt;ScanItem&gt; Items { get; set; } = new List&lt;ScanItem&gt;(); 如果 Linq-to-SQL 尝试在 null 集合上调用 Sum,您将收到错误消息。
  • @crgolden 你说得对,包含是不必要的,集合不应为空,但它没有帮助。
  • 是的,我相信这可能是因为内部Sum 在这里本质上被视为一种回调,因为它本质上是Func 内的Func。在构建最终的 select

标签: c# entity-framework asp.net-core entity-framework-core ef-core-3.1


【解决方案1】:

EF Core 仍然无法转换 GroupBy 结果(分组)上的嵌套聚合。

您必须使用GroupBy(或查询语法group element by key中的element)的元素选择器预先计算嵌套聚合:

from scan in Scans
group new { scan.ScanDate, Qty = scan.Items.Sum(i => i.Qty) } // <--
by new { scan.ScanUser.Name, scan.ScanUser.Id } into g
select new
{
    UserId = g.Key.Id,
    Name = g.Key.Name,
    LastSyncTime = g.Max(x => x.ScanDate),
    ScanItems = g.Sum(x => x.Qty) // <--
}

**更新:** 对于 SqlServer,上述 LINQ 查询转换为 SQL 查询,如下所示:

 SELECT [s1].[Id] AS [UserId], [s1].[Name], MAX([s0].[ScanDate]) AS [LastSyncTime], SUM((
      SELECT SUM([s].[Qty])
      FROM [ScanItem] AS [s]
      WHERE [s0].[Id] = [s].[InventoryScanId])) AS [ScanItems]
  FROM [Scan] AS [s0]
  INNER JOIN [ScanUser] AS [s1] ON [s0].[ScanUserId] = [s1].[Id]
  GROUP BY [s1].[Name], [s1].[Id]

正如评论中提到的那样会产生 SQL 执行异常“无法对包含聚合或子查询的表达式执行聚合函数。”

所以您确实需要另一种方法 - 在分组之前使用left join 将结果集展平,然后对该集执行分组/聚合:

from scan in Scans
from item in scan.Items.DefaultIfEmpty() // <-- left outer join
group new { scan, item } by new { scan.ScanUser.Name, scan.ScanUser.Id } into g
select new
{
    UserId = g.Key.Id,
    Name = g.Key.Name,
    LastSyncTime = g.Max(x => x.scan.ScanDate),
    ScanItems = g.Sum(x => (double?)x.item.Qty) ?? 0
};

现在转换为希望有效的 SqlServer SQL 查询:

  SELECT [s1].[Id] AS [UserId], [s1].[Name], MAX([s].[ScanDate]) AS [LastSyncTime], COALESCE(SUM([s0].[Qty]), 0.0E0) AS [ScanItems]
  FROM [Scan] AS [s]
  LEFT JOIN [ScanItem] AS [s0] ON [s].[Id] = [s0].[InventoryScanId]
  INNER JOIN [ScanUser] AS [s1] ON [s].[ScanUserId] = [s1].[Id]
  GROUP BY [s1].[Name], [s1].[Id]

【讨论】:

  • 感谢它看起来很有希望,但它现在给出了 SqlException:SqlException: Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
  • @Perrier Bummer。解决 EF Core 查询限制只是为了达到 SQL 查询限制 :-( 无论如何,请参阅更新,希望它应该可以工作。
猜你喜欢
  • 2019-12-22
  • 2020-11-06
  • 2019-04-27
  • 2020-03-10
  • 1970-01-01
  • 2016-03-20
  • 2020-11-29
  • 1970-01-01
相关资源
最近更新 更多