webentwicklung-frage-antwort-db.com.de

Entity Framework - Linq-Abfrage mit Sortierung nach und Gruppierung nach

Ich habe Measurement-Objekte mit den relevanten Eigenschaften CreationTime (DateTime) und Reference (String) und einigen anderen Werten.

Ich möchte eine effiziente linq-Abfrage an eine DbContext schreiben 

  • gruppiert meine Measurement-Objekte nach einer gegebenen Reference
  • ordnet die Gruppen entweder über die CreationTime-Eigenschaft der ersten Measurement aus der Gruppe oder den Durchschnitt der CreationTime-Eigenschaften an
  • begrenzt die Abfrage auf die aktuellste numOfEntries

(In einem späteren Schritt berechne ich gemittelte Werte über diese zurückgegebenen Gruppen, aber ich denke, das ist für mein Problem nicht relevant.)

Der DbContext selbst hat einen DbSet<Measurement> namens Measurements, der alle Measurement-Objekte enthält.

Ich habe die folgende Abfrage gefunden, die zu einer Liste von Gruppen führt, die zwar korrekt geordnet ist, jedoch einige Gruppen dazwischen fehlen.

var groupByReference = (from m in context.Measurements
                          orderby m.CreationTime
                          group m by new { m.Reference } into g
                          select g).Take(numOfEntries).ToList();

Wie wähle ich die "neuesten" Gruppen von Measurements korrekt aus?

Ich verwende Entity Framework 4.4 mit einer MySQL-Datenbank (MySql Connector/Net 6.6.4). Dieses Beispiel ist vereinfacht, ich kann detaillierter eingehen und/oder, wenn nötig, ein Beispiel geben.

Vielen Dank!

11
zufallsfund

Es ist die Methodensyntax (die ich leichter zu lesen finde), aber das könnte es tun

Aktualisierter Kommentar zum Beitrag

Verwenden Sie .FirstOrDefault() anstelle von .First()

In Bezug auf den Durchschnittswert der Daten müssen Sie diese Reihenfolge für den Moment fallen lassen, da ich momentan nicht zu einem IDE gelangen kann

var groupByReference = context.Measurements
                              .GroupBy(m => m.Reference)
                              .Select(g => new {Creation = g.FirstOrDefault().CreationTime, 
//                                              Avg = g.Average(m => m.CreationTime.Ticks),
                                                Items = g })
                              .OrderBy(x => x.Creation)
//                            .ThenBy(x => x.Avg)
                              .Take(numOfEntries)
                              .ToList();
11
NinjaNye

Sie können versuchen, das Ergebnis von GroupBy und Take in ein Enumerable umzuwandeln und dann den Rest zu verarbeiten (basierend auf der von NinjaNye bereitgestellten Lösung) 

var groupByReference = (from m in context.Measurements
                              .GroupBy(m => m.Reference)
                              .Take(numOfEntries).AsEnumerable()
                               .Select(g => new {Creation = g.FirstOrDefault().CreationTime, 
                                             Avg = g.Average(m => m.CreationTime.Ticks),
                                                Items = g })
                              .OrderBy(x => x.Creation)
                              .ThenBy(x => x.Avg)
                              .ToList() select m);

Ihre SQL-Abfrage würde ähnlich aussehen (abhängig von Ihrer Eingabe)

SELECT TOP (3) [t1].[Reference] AS [Key]
FROM (
    SELECT [t0].[Reference]
    FROM [Measurements] AS [t0]
    GROUP BY [t0].[Reference]
    ) AS [t1]
GO

-- Region Parameters
DECLARE @x1 NVarChar(1000) = 'Ref1'
-- EndRegion
SELECT [t0].[CreationTime], [t0].[Id], [t0].[Reference]
FROM [Measurements] AS [t0]
WHERE @x1 = [t0].[Reference]
GO

-- Region Parameters
DECLARE @x1 NVarChar(1000) = 'Ref2'
-- EndRegion
SELECT [t0].[CreationTime], [t0].[Id], [t0].[Reference]
FROM [Measurements] AS [t0]
WHERE @x1 = [t0].[Reference]
1
user3373870

Verschieben Sie den order by nach group by:

var groupByReference = (from m in context.Measurements
                        group m by new { m.Reference } into g
                        order by g.Avg(i => i.CreationTime)
                        select g).Take(numOfEntries).ToList();
0
MarcinJuraszek

Ihre Anforderungen sind überall, aber dies ist die Lösung für mein Verständnis von ihnen:

So gruppieren Sie nach der Reference-Eigenschaft:

var refGroupQuery = (from m in context.Measurements
            group m by m.Reference into refGroup
            select refGroup);

Jetzt sagen Sie, Sie möchten die Ergebnisse durch "neueste numOfEntries" begrenzen - ich meine damit, dass Sie die zurückgegebenen Messungen begrenzen wollen ... in diesem Fall:

var limitedQuery = from g in refGroupQuery
                   select new
                   {
                       Reference = g.Key,
                       RecentMeasurements = g.OrderByDescending( p => p.CreationTime ).Take( numOfEntries )
                   }

So ordnen Sie Gruppen nach der Erstellungszeit der ersten Messung (Hinweis: Sie sollten die Messungen anordnen; wenn Sie den frühesten CreationTime-Wert erhalten möchten, ersetzen Sie "g.SomeProperty" durch "g.CreationTime"):

var refGroupsOrderedByFirstCreationTimeQuery = limitedQuery.OrderBy( lq => lq.RecentMeasurements.OrderBy( g => g.SomeProperty ).First().CreationTime );

Verwenden Sie zum Sortieren von Gruppen nach durchschnittlicher CreationTime-Eigenschaft die Ticks-Eigenschaft der DateTime-Struktur:

var refGroupsOrderedByAvgCreationTimeQuery = limitedQuery.OrderBy( lq => lq.RecentMeasurements.Average( g => g.CreationTime.Ticks ) );
0
Moho