F#3.0查询中的多列

试试F#3.0,当涉及到多列分组的时候,碰到了一些问题。 显而易见的事情是尝试

query { for d in context.table do groupBy (d.col1,d.col2) into g select (g.Key) } 

但是我得到了“LINQ to Entities支持只有无参数的构造函数和初始化器”。 例外。

我似乎无法find一个MSDN上的例子

http://msdn.microsoft.com/en-us/library/hh225374(v=vs.110).aspx

http://msdn.microsoft.com/en-us/library/hh361035(v=vs.110).aspx

我意识到我的问题类似于“ F#中的entity framework和匿名types ”,但它似乎是powerpack / F#2.x的重点,我希望F#3.0有一个优雅的答案…任何想法?

更新:

我在阅读Brian的post时遇到了CLIMutable属性:

http://blogs.msdn.com/b/fsharpteam/archive/2012/07/19/more-about-fsharp-3.0-language-features.aspx

我非常乐观,所以我尝试了

 [<CLIMutable>] type MyRecord = { Column1 : int; Column2 : int } query { for d in context.table do groupBy {Column1 = col1; Column2 = col2} into g select (g.Key) } 

不幸的是我得到了完全相同的例外。

以下是在C#中使用多列进行分组的示例,并将其转换为f#(过分偏执的pipe理让我重命名所有内容,但我相信我一直保持一致):

(该数据库是由SqlMetal生成的,GetSummedValuesResult是一个F#loggingtypes)

C#

 public static class Reports { public static IReadOnlyList<GetSummedValuesResult> GetSummedValues(TheDatabase db, DateTime startDate, DateTime? endDate) { var query = from sv in db.SomeValues where (sv.ADate >= startDate && sv.ADate <= (endDate ?? startDate)) group sv by new { sv.ADate, sv.Owner.Name } into grouping select new GetSummedValuesResult( grouping.Key.ADate, grouping.Key.Name, grouping.Sum(g => g.Value) ); return query.ToList(); } } 

F#

 type Reports() = static member GetSummedValues (db:TheDatabase) startDate (endDate:Nullable<DateTime>) = let endDate = if endDate.HasValue then endDate.Value else startDate let q = query { for sv in db.SomeValues do where (sv.ADate >= startDate && sv.ADate <= endDate) let key = AnonymousObject<_,_>(sv.ADate, sv.Owner.Name) groupValBy sv key into grouping select { ADate = grouping.Key.Item1; AName = grouping.Key.Item2; SummedValues = grouping.Sum (fun (g:TheDatabaseSchema.SomeValues) -> g.Value) } } List(q) :> IReadOnlyList<GetSummedValuesResult> 

所以使用的是Microsoft.FSharp.Linq.RuntimeHelpers.AnonymousObject

请注意,您不应该使用Seq模块进行聚合function!

 SummedValues = grouping |> Seq.sumBy (fun g -> g.SomeValues) 

虽然这会起作用,但它会在客户端进行聚合,而不是制定适当的SQL。

我在第一个链接中看到这个,我想这是你想要的:

 query { for student in db.Student do groupValBy student.Name student.Age into g select (g, g.Key, g.Count()) } 

当使用groupBy时,你需要select一个聚合函数(例如count,sum,avg …)。

首先你必须记住,查询在某个时刻被翻译成实际的SQL 。 看来,linq不支持使用多个组键作为Tuple<> 。 因此,在数据库调用完成之后,任何到Tuple<>转换都必须完成。

其次,您应该能够通过在各自的键上执行多个分组来实现多个键组合

 query { for d1 in context.table do groupBy d1.col1 into g1 for d2 in g1 do groupBy d2.col2 into g2 select g2 } 

如果语法不是100%,请对我施以怜悯,因为F#不是我的母语:)然而,这个概念应该工作得很好。

 open Microsoft.FSharp.Linq.RuntimeHelpers open System.Linq query { for d in context.table do let t = MutableTuple<_,_>(Item1=d.col1,Item2=d.col2) groupValBy dt into g select (g.Key,g.Count()) } 
 //in F# 3.0 open Microsoft.FSharp.Linq.RuntimeHelpers open Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter open System.Linq //[<CLIMutable>] //type MyRecord = { Column1 : int; Column2 : int } // require constructor in F# // groupBy is not valid type T(column1 : int, column2 : int) member val Colum1=colum1 with get,set membre val Colum2=colum2 with get,set query { for d in context.table do groupValBy d (NewAnonymousObjectHelper(T(d.Colum1,d.Colume2))) into g select (g.Key) } 
 query { for d in context.table do groupBy (new {d.col1, d.col2}) into g select (g.Key) }