Entityframework Core 3 linq 表达式无法翻译

Jac*_*kal 6 c# entity-framework-core

我刚刚升级到 EF 3 和我曾经工作的查询之一,现在给出了一个例外

   ProductionRecords = _context.ProductionRecords
          .Where(r => r.DataCriacao.Date == DateTime.Now.Date)
            .Select(pr => new ProductionRecordViewModel
            {
                Id = pr.Id,
                Operador = pr.Operador,
                DataCriacao = pr.DataCriacao,
                Celula = pr.Celula.Name,
                Turno = pr.Turno.Name,
                TotalPecasSemDefeito = pr.ReferenceRecords.Sum(c => c.Quantity),
                TotalPecasComDefeito = pr.DefectRecords.Sum(c => c.Quantidade),
                TotalTempoParado = pr.StopRecords.Sum(c => Convert.ToInt32(c.Duration.TotalMinutes)),
            })
          .AsNoTracking()
          .ToList();
Run Code Online (Sandbox Code Playgroud)

当我试图用时间跨度和持续时间对集合求和时会发生异常......

我现在该怎么处理?

这是例外

InvalidOperationException: LINQ 表达式 '(EntityShaperExpression: EntityType: StopRecord ValueBufferExpression: (ProjectionBindingExpression: EmptyProjectionMember) IsNullable: False ).Duration.TotalMinutes' 无法翻译。以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。有关详细信息,请参阅https://go.microsoft.com/fwlink/?linkid=2101038

tim*_*mur 6

EF3 中发生了重大变化,除非在查询链的最末端(您Convert.ToInt32(c.Duration.TotalMinutes)可能依赖它),否则不会自动恢复到客户端评估。

尝试像这样重写您的查询:

 ProductionRecords = _context.ProductionRecords
      .Where(r => r.DataCriacao.Date == DateTime.Now.Date)
        .AsNoTracking()
        .AsEnumerable()
        .Select(pr => new ProductionRecordViewModel
        {
            Id = pr.Id,
            Operador = pr.Operador,
            DataCriacao = pr.DataCriacao,
            Celula = pr.Celula.Name,
            Turno = pr.Turno.Name,
            TotalPecasSemDefeito = pr.ReferenceRecords.Sum(c => c.Quantity),
            TotalPecasComDefeito = pr.DefectRecords.Sum(c => c.Quantidade),
            TotalTempoParado = pr.StopRecords pr.StopRecords.Sum(c => Convert.ToInt32(c.Duration.TotalMinutes)),
        })
      .ToList();
Run Code Online (Sandbox Code Playgroud)

UPD正如评论中正确指出的那样 - 这基本上会将.Select评估推迟到客户端。这可能会导致性能问题。这种行为很可能是最初对 EF Core 3 进行此更改的原因。

我没有足够的细节向您推荐合适的解决方案,但似乎您无法真正摆脱加载StopRecords所有结果。这就是编写自定义方法转换器可以帮助您的地方。关于如何做到这一点,请参阅我的其他答案。我快速检查了 EF Core 3 源代码,似乎IMethodCallTranslator仍然存在。这意味着您很有可能构建一个自定义函数,将日期转换为 SQL 中的 TotalMinutes。

  • 值得注意的是,这可能会导致对 Select 子句中求和的三个表中的每一个进行额外的查询。可能N+1次 (2认同)
  • @IvanStoev 公平地说,我添加了更多背景信息和建议的方法来正确解决根本原因。希望这能为 OP 和社区带来足够的价值 (2认同)

Jer*_*man 6

与其教 EF Core 如何对时间跨度求和,不如向数据库添加计算列怎么样?

        public TimeSpan Duration { get; set; }
        public int Minutes { get; }


        entity.Property(e => e.Minutes)
            .HasComputedColumnSql("DATEDIFF(MINUTE, 0, Duration)");
Run Code Online (Sandbox Code Playgroud)