在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()的调用来显式切换到客户端评估。
如何按日期分组?
因此,在C#中,当您使用LINQ-to-SQL 查询时,您正在使用上的扩展方法IQueryable<T>
。如果我们看一下再说的签名,该GroupBy
方法,你会看到,函数签名实际上是
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#有自己的SQL查询理解生成器。它使您可以编写类似于seq
可转换为SQL查询的计算表达式。这就像您期望的那样工作。
query {
for record in db do
select record
}