【问题标题】:Sum of Timespans using LINQ to Entities使用 LINQ to Entities 的时间跨度总和
【发布时间】:2018-03-02 18:51:31
【问题描述】:

我有一个按 LINQ 调用的组,然后将 PossessionTime.Tick 值求和为一个新的时间跨度。这以前工作得很好,但现在我需要将集合保留为 IQueryable 而不是 List。

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

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

LINQ 仅支持无参数构造函数和初始化程序 到实体。

我已经研究过使用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,因为我在分页中使用它。

【问题讨论】:

  • 好好看看docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/…。您提到的问题可能与这里的代码无关,但事实上此时需要一个具体的值,即总和,因此所有惰性链都被评估。也许是分组?如果没有更多代码,我不会知道。什么是调用堆栈?
  • 您需要显示IQueryablex 是什么?它是如何定义的?
  • 现在更新@NetMage

标签: c# .net linq linq-to-entities timespan


【解决方案1】:

我认为您无法通过使用直接 linq 的 Linq-to-SQL/EF 轻松做到这一点,因为 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 类更改为存储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)
    });

【讨论】:

  • 我认为第二个选项会很好用,谢谢!
  • 其实第二种方法行不通。我犯了同样的错误。 LINQ to Entities 不支持指定类型成员“Ticks”
  • 如果 EF 不能翻译 DateTime.Ticks 那么你有几个选择。在执行select 之前,您可以先考虑分页。否则,可能有一种方法可以将“插件”添加到 EF 中,告诉它如何将DateTime.Ticks 转换为 sql。或者您可以更新您的数据库以始终将 PossesionTime 存储为 Ticks 而不是 DateTime。
  • 是的,存储为刻度听起来是唯一的选择,因为我需要能够对 iqueryable 进行排序并对其进行分页
【解决方案2】:

您可以使用 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 秒,否则,您需要将总和转换为小时、分钟和秒。

【讨论】:

  • 我不能做 .Ticks 因为它是一个 IQueryable 去实体。 LINQ to Entities 不支持指定的类型成员“Ticks”
  • 数据库和EF中PossessionTime的类型是什么?我以为您可以使用Ticks,因为您接受了使用Ticks 的答案...但现在我发现那行不通。
  • 它在数据库中的时间跨度,但我现在可能不得不更改它。
  • 我将表达式更改为使用DiffSeconds 来计算秒数,假设您可以将TimeSpan.Zero 常量传递给SQL。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多