c# - ef-core 嵌套表聚合函数给出 "NavigationExpandingExpressionVisitor failed"

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

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

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 给出了以下错误:

Processing of the LINQ expression 'AsQueryable((Unhandled parameter: x).Items)' by 'NavigationExpandingExpressionVisitor' failed

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))
    }

如何在不在客户端评估的情况下对嵌套表的属性运行聚合函数?

最佳答案

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

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

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]

关于c# - ef-core 嵌套表聚合函数给出 "NavigationExpandingExpressionVisitor failed",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59735196/

相关文章:

c# - 使用类在 MVVM Light 中引用 View 模型定位器

asp.net-mvc - 为什么实体类上的可审计属性不在数据库中生成列?

attributes - .NET Core 模型在发布请求中与连字符属性名称绑定(bind)

c# - 如何简化基本凯撒移位加密算法 C# 的实现?

c# - 你如何使 'if' 语句执行 "nothing"?

c# - 任何自动克隆数据对象的方法?

c# - 从 EF6 中的多对多连接表返回一列

c# - 比较两个对象的值而不列出每一列

c# - Asp.net Core Identity 单元测试 Controller 操作

asp.net-core - 您如何从 Azure Active Directory 获取脚手架 Weather Forecast Api 将接受的访问 token ?