实体框架核心组截止日期

时间:2020-01-31 14:13:14

标签: linq f# entity-framework-core

在C#中,您可以按.Date分组:

db.History.GroupBy(x => x.Timestamp.Date)
          .Select(g => new { key = g.Key, aggregate = g.Count() })

但是,等效的F#不起作用:

db.History.GroupBy(fun x -> x.Timestamp.Date)
          .Select(fun g -> { Date = g.Key; Count = g.Count()} )

相关记录:

type DateCount = {
    Date: DateTime
    Count: int
}

它引发以下错误:

System.InvalidOperationException:无法转换LINQ表达式'DbSet<HistoryEntity> .GroupBy( source: h => copyOfStruct => copyOfStruct.Date.Invoke(h.Timestamp), keySelector: h => h)'。可以以可翻译的形式重写查询,也可以通过插入对AsEnumerable(),AsAsyncEnumerable(),ToList()或ToListAsync()的调用来显式切换到客户端评估。

如何按日期分组?

2 个答案:

答案 0 :(得分:5)

因此在C#中,当您使用LINQ-to-SQL 查询时,您正在使用IQueryable<T>上的扩展方法。如果我们看一下GroupBy method的签名,您会发现函数签名实际上是

IQueryable<TSource>.GroupBy<TSource,TKey>(Expression<Func<TSource,TKey>> keySelector)

这是怎么回事? Expression<>是一种特殊类型-当C#编译器发现Expression<>类型时,编译器将构建AST并传递AST对象(类型为Expression<Func<>>),而不是通常的委托。底层功能将检查AST并构建最终需要的查询表达式,例如用于查询数据库的SQL。

您可以自己尝试:

Expression<Func<int>> getRandom = () => 4; //random, chosen by fair dice roll

您可以检查getRandom的属性以查看AST。 由于魔术发生在C#编译器中,因此当您在F#中进行操作时,这不会消除它。

更详细地讲,F#编译器可以识别Expression<>,但是可以通过应用隐式F#引号来实现-这样您就可以得到F#引号包装的方法调用,该方法调用可以转换为C#表达式树。 (很抱歉,那是在运行。)

F#具有自己的查询理解builder for SQL。它使您可以编写类似于seq的计算表达式,以转换为SQL查询。这就像您期望的那样工作。

query {
    for record in db do
    select record
}

答案 1 :(得分:0)

在查询表达式中使用时,按.Date分组。

query {
    for h in db.History do
    groupValBy h h.Timestamp.Date into g
    select {
        Date  = g.Key
        Count = g.Count()
    }
}

here被盗的代码。

如果有人可以解释为什么查询表达式起作用但LINQ版本不起作用,我将不胜感激:)