agrupado por columnas mĂșltiples en una consulta F#3.0
f#-3.0 (7)
Simplemente probando F # 3.0 y golpear un poco de una pared cuando se trata de agrupar por múltiples columnas. Lo obvio para intentar era
query {
for d in context.table do
groupBy (d.col1,d.col2) into g
select (g.Key)
}
Pero obtengo un "Solo los constructores y los inicializadores sin parámetros son compatibles con LINQ to Entities". excepción.
Parece que no puedo encontrar un ejemplo en 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
Y me doy cuenta de que mi pregunta es similar a " Entity Framework and Anonymous Types in F # ", pero parece ser powerpack / F # 2.x centrado y espero que F # 3.0 tenga una respuesta elegante ... ¿Alguna idea?
ACTUALIZAR:
Me encontré con el atributo CLIMutable al leer la publicación de Brian en:
http://blogs.msdn.com/b/fsharpteam/archive/2012/07/19/more-about-fsharp-3.0-language-features.aspx
Yo era bastante optimista, así que lo intenté
[<CLIMutable>]
type MyRecord = { Column1 : int; Column2 : int }
query {
for d in context.table do
groupBy {Column1 = col1; Column2 = col2} into g
select (g.Key)
}
Desafortunadamente recibo la misma excepción exacta.
Al usar groupBy, necesita seleccionar una función de agregación (por ejemplo, count, sum, avg ...).
Primero debe recordar que una consulta se traduce en SQL
real en algún momento. Parece que linq no admite el uso de varias claves de grupo como Tuple<>
. Por lo tanto, cualquier transformación en Tuple<>
debe realizarse después de que se haya completado la llamada a la base de datos.
En segundo lugar, debe poder lograr múltiples agrupaciones de teclas realizando múltiples agrupaciones detrás de las otras en las claves respectivas:
query {
for d1 in context.table do
groupBy d1.col1 into g1
for d2 in g1 do
groupBy d2.col2 into g2
select g2
}
Por favor, ten piedad de mí si la sintaxis no es 100% ya que F # no es mi lengua materna :) Sin embargo, el concepto debería funcionar bien.
query {
for d in context.table do
groupBy (new {d.col1, d.col2}) into g
select (g.Key)
}
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())
}
Veo esto en el primero de sus enlaces, creo que es lo que quiere:
query {
for student in db.Student do
groupValBy student.Name student.Age into g
select (g, g.Key, g.Count())
}
El siguiente es un ejemplo de columnas múltiples que se usan para agrupar en c # y convertir a f # (la administración excesivamente paranoica me ha hecho cambiar el nombre de todo, pero creo que he sido coherente):
(TheDatabase fue generado por SqlMetal, GetSummedValuesResult es un tipo de registro F #)
do#
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>
Entonces, lo que hay que usar es Microsoft.FSharp.Linq.RuntimeHelpers.AnonymousObject
Tenga en cuenta que no debe usar el módulo Seq para las funciones de agregación.
SummedValues = grouping |> Seq.sumBy (fun g -> g.SomeValues)
Aunque esto FUNCIONARÁ, hace la agregación en el lado del cliente, en lugar de formular el SQL apropiado.
//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)
}