web-dev-qa-db-fra.com

Entity Framework - Requête Linq avec ordre par et groupe par

J'ai des objets Measurement avec les propriétés pertinentes CreationTime (DateTime) et Reference (String), ainsi que d'autres valeurs.

Je voudrais écrire une requête linq efficace à un DbContext qui 

  • regroupe mes objets Measurement par une Reference donnée
  • classe les groupes en fonction de la propriété CreationTime de la première Measurement du groupe ou de la moyenne des propriétés CreationTime
  • limite la requête à la plus récente numOfEntries

(Dans une étape ultérieure, je calcule les valeurs moyennes sur ces groupes, mais je suppose que cela n’est pas pertinent pour mon problème.)

Le DbContext lui-même a un DbSet<Measurement> appelé Measurementsholding tous les objets Measurement.

Je suis venu avec la requête suivante, qui résulte en une liste de groupes qui est ordonnée correctement mais il manque certains groupes entre les deux.

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

Comment sélectionner les groupes "les plus récents" de Measurements correctement?

J'utilise Entity Framework 4.4 avec une base de données MySQL (MySql Connector/Net 6.6.4). Cet exemple est simplifié, je peux entrer plus en détail et/ou donner un exemple si nécessaire.

Merci!

11
zufallsfund

C'est la syntaxe de la méthode (que je trouve plus facile à lire) mais cela pourrait le faire

Mise à jour du commentaire

Utilisez .FirstOrDefault() au lieu de .First()

En ce qui concerne la moyenne des dates, vous devrez peut-être abandonner cette commande pour le moment car je ne peux pas me rendre à un IDE pour le moment

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

Vous pouvez essayer de convertir le résultat de GroupBy et de Prendre dans un Enumerable, puis de traiter le reste 

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);

Votre requête SQL serait similaire (en fonction de votre saisie) cette

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

Essayez de déplacer le order by après le 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

Vos exigences sont partout, mais voici la solution pour les comprendre:

Pour grouper par propriété de référence:

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

Maintenant, vous dites que vous voulez limiter les résultats par "les plus récents numéros". Je suppose que cela signifie que vous souhaitez limiter les mesures renvoyées ... dans ce cas:

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

Pour classer les groupes par ordre chronologique au moment de la création de la mesure (notez que vous devez commander les mesures; si vous voulez la valeur CreationTime la plus ancienne, remplacez "g.SomeProperty" par "g.CreationTime"):

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

Pour classer les groupes en fonction de CreationTime moyen, utilisez la propriété Ticks de la structure DateTime:

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