linq 使用EF Core获取平均时间跨度

yyyllmsg  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(114)

我正在使用EF Core 7和Postgres。我需要计算一些统计数据:

public record Job(string Name, DateTime Started, DateTime Stopped);
public record Stats(TimeSpan Min, TimeSpan Mean, TimeSpan Max);

// 

var stats = await _context
  .Jobs
  .GroupBy(x => x.Name)
  .Select(x => new Stats(
    x.Min(y => y.Stopped - y.Started), 
    new TimeSpan((long)x.Select(y => y.Stopped - y.Started).Select(x => x.Ticks).Average()), 
    x.Max(y => y.Stopped - y.Started)
  ))
  .ToListAsync();

字符串
普通的子查询看起来很奇怪,但是可以作为非EF查询使用。但是EF拒绝:
InvalidOperationException:LINQ表达式“SystemalGroupByShaperExpression:
...
.Average()'无法翻译。请以可翻译的格式重写查询,或切换到客户端评估
我认为EF不能翻译Ticks。我在EF.Functions中没有找到任何有用的东西。
我不想在客户端上做这个,我想在数据库上做这个,因为数据集很大。这可能吗?

4xy9mtcn

4xy9mtcn1#

你是对的,实体框架不理解TimeSpan. Ticks的概念。
但是,类DbFunctions有几个方法可以获取两个DateTimes之间的(纳秒)秒/分钟/小时等数。选择您需要的精度,例如:

var stats = await _context
    .Jobs
    .GroupBy(x => x.Name)
    .Select(x=> new
        {
            Min = x.Select(y => y.Stopped - y.Started).Min(),
            AverageTimeSpanMilliSect = x.Select(y =>
                    DbFunctions.DiffMilliSeconds(y.Stopped, y.Started)
                .Average(),
            Max = x.Select(y => y.Stopped - y.Started).Max(),
        })

    // Move the selected data to your local process and convert to Stats
    .AsEnumerable()
    .Select(x => new Stats(x.Min, 
                           TimeSpan.FromMilliSeconds(x.AverageTimeSpanMilliSec,
                           x.Max));

字符串

plicqrtu

plicqrtu2#

除了客户端eval,还可以使用提供程序特定的函数(我使用的是Postgres):

var stats = await _context
  .Jobs
  .GroupBy(x => x.Name)
  .Select(x => new Stats(
    x.Min(y => y.Stopped - y.Started), 
    EF.Functions.Average(x.Select(y => y.Stopped - y.Started)),  // <---
    x.Max(y => y.Stopped - y.Started)
  ))
  .ToListAsync();

字符串
但是,该属性必须是可空的:

public record Stats(TimeSpan Min, TimeSpan? Mean, TimeSpan Max);


问题是Ticks(当前)不支持。这是requested here,请投票支持。

相关问题