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。
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。
与其教 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)