使用 LINQ to Entities 的时间跨度总和

Sum of Timespans using LINQ to Entities

我通过 LINQ 调用进行分组,然后将 PossessionTime.Tick 值相加作为新的时间跨度。这以前工作得很好,但现在我需要将集合保留为 IQueryable 而不是 List。

PossessionTime = new TimeSpan(x.Sum(p => p.PossessionTime.Ticks))

通过此更改,我现在收到以下错误:

Only parameterless constructors and initializers are supported in LINQ to Entities.

我研究过使用 DbFunctions,但我认为它没有任何可以帮助我的东西。

我唯一的选择是将列表移入内存并在内存中求和时间吗?

IQueryable如下:

return stats.GroupBy(x => x.Id).OrderByDescending(x => x.Sum(a => a.Kills))
            .Select(x => new WeaponItem
            {         
                PossessionTime = new TimeSpan(x.Sum(p => p.PossessionTime.Ticks))
            });

我需要将它保留为 IQueryable,因为我在分页中使用它。

我不认为你能够通过 Linq-to-SQL/EF 使用直接的 linq 很容易地做到这一点,因为 PossionTime.Ticks。 C# DateTime 操作在转换为 sql 操作时非常有限。

如果您的唯一要求是支持分页,您可以通过在分页操作后进行 WeaponItem 投影并将页面拉入内存来让自己轻松实现:

return stats
   .GroupBy(x => x.Id)
   .OrderByDescending(x => x.Sum(a => a.Kills))
   // paginatation
   .Skip(pageNumber * pageSize)
   .Take(pageSize)
   // pull paged result set into memory to make life easy
   .ToList()
   // transform into WeaponItem
   .Select(x => new WeaponItem
   {         
        PossessionTime = new TimeSpan(x.Sum(p => p.PossessionTime.Ticks))
   });

或者,您可以将 WeaponItem class 更改为存储 PossesionTimeTicks,然后提供 PossesionTime 作为计算 属性。这可能会绕过限制:

public class WeaponItem
{
    public long PosseionTimeTicks {get;set;}
    public TimeSpan PossesionTime {get{ return new TimeSpan(PosseionTimeticks); }
}

那么我认为您应该能够将查询保留为 IQueryable:

return stats
    .GroupBy(x => x.Id)
    .OrderByDescending(x => x.Sum(a => a.Kills))
    .Select(x => new WeaponItem
    {         
        PossessionTimeTicks = x.Sum(p => p.PossessionTime.Ticks)
    });

您可以使用 DBFunctions.CreateTime 从 HMS 创建一个 TimeSpan,这样您就可以进行数学转换:

return stats.GroupBy(x => x.Id).OrderByDescending(x => x.Sum(a => a.Kills))
            .Select(x => new WeaponItem
            {         
                PossessionTime = DBFunctions.CreateTime(0, 0, x.Sum(p => DBFunctions.DiffSeconds(p.PossessionTime, TimeSpan.Zero)))
            });

注意: 我假设 CreateTime 将接受秒参数 >= 60 秒,否则,您需要将总和转换为小时、分钟和秒。