Groupby несколько столбцов в запросе F # 3.0
Просто попробуйте F # 3.0 и нанесите немного стены, когда дело доходит до группировки по нескольким столбцам. Очевидным было бы попробовать
query {
for d in context.table do
groupBy (d.col1,d.col2) into g
select (g.Key)
}
Но я получаю "Только конструкторы без параметров и инициализаторы поддерживаются в LINQ для Entities". исключение.
Я не могу найти пример в 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
И я понимаю, что мой вопрос похож на
"Entity Framework и анонимные типы в F #", но кажется, что Powerpack/F # 2.x сосредоточен, и я надеюсь, что F # 3.0 имеет элегантный ответ... Любые идеи?
UPDATE:
Я наткнулся на атрибут 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)
}
К сожалению, я получаю то же самое исключение.
Ответы
Ответ 1
Ниже приведен пример нескольких столбцов, используемых для группировки в С# и преобразованных в f # (чрезмерно параноидальное управление заставило меня переименовать все, но я считаю, что я был последовательным):
(TheDatabase была сгенерирована SqlMetal, GetSummedValuesResult - это тип записи F #)
С#
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();
}
}
е #
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 для функций агрегации!
SummedValues = grouping |> Seq.sumBy (fun g -> g.SomeValues)
Хотя это WILL WORK, он выполняет агрегацию на стороне клиента, а не формулирует соответствующий SQL.
Ответ 2
Я вижу это в первой из ваших ссылок, я думаю, что это то, что вы хотите:
query {
for student in db.Student do
groupValBy student.Name student.Age into g
select (g, g.Key, g.Count())
}
Ответ 3
Сначала вы должны помнить, что запрос переводится в фактический 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 # не является моим родным языком:) Концепция, однако, должна работать нормально.
Ответ 4
При использовании groupBy вам нужно выбрать агрегирующую функцию (например, count, sum, avg...).
Ответ 5
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 d t into g
select (g.Key,g.Count())
}
Ответ 6
//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)
}
Ответ 7
Очевидно, что может быть решение, указанное в, с использованием комбинации groupValBy
и AnonymousObject<_,_>
из Linq RuntimeHelpers.
ех.
query {
for d in context.table do
let key = AnonymousObject<_,_>(d.col1,d.col2)
groupValBy d key into g
select (g.Key)
}
Ответ 8
query {
for d in context.table do
groupBy (new {d.col1, d.col2}) into g
select (g.Key)
}