3

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

如何按日期分组?

4

2 回答 2

5

因此,在 C# 中,当您使用 LINQ-to-SQL查询时,您使用的是IQueryable<T>. 如果我们查看 say, the 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# 引号来实现的——因此您会获得一个转换为 C# 表达式树的 F# 引号包装的方法调用。(对不起,如果,那是在运行。)

F# 有自己的 SQL 查询理解生成器。它使您可以编写seq转换为 SQL 查询的计算表达式。这就像你期望的那样工作。

query {
    for record in db do
    select record
}
于 2020-01-31T17:41:50.800 回答
0

Group by.Date在查询表达式中使用时起作用。

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

从这里窃取的代码。

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

于 2020-01-31T14:13:14.623 回答