Entity Framework - запрос Linq с порядком и группой по

У меня есть Measurement Объекты с соответствующими свойствами CreationTime (DateTime) и Reference (String) и некоторые другие значения.

Я хотел бы написать эффективный запрос linq к DbContext, который

  • группирует объекты Measurement по заданному Reference
  • упорядочивает группы с помощью свойства CreationTime первого Measurement из группы или среднего значения свойств CreationTime
  • ограничивает запрос до последнего numOfEntries

(На следующем этапе я вычисляю усредненные значения по этим возвращенным группам, но думаю, что это не относится к моей проблеме.)

Сам DbContext имеет DbSet<Measurement>, называемый Measurements, содержащий все Measurement объекты.

Я придумал следующий запрос, который приводит к упорядоченному списку групп, но между ними отсутствуют некоторые группы.

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

Как правильно выбрать "последние" группы Measurement?

Я использую Entity Framework 4.4 с базой данных MySQL (MySql Connector/Net 6.6.4). Этот пример упрощен, я могу более подробно и/или привести пример, если это необходимо.

Спасибо!

Ответ 1

Этот синтаксис метода (который мне легче читать), но это может сделать это

Обновлен комментарий к сообщению

Используйте .FirstOrDefault() вместо .First()

Что касается средних дат, возможно, вам придется отказаться от этого заказа на данный момент, так как я не могу добраться до IDE в настоящий момент.

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

Ответ 2

Вы можете попытаться выполнить результат GroupBy и сначала принять в Enumerable, а затем обработать остальные (построив решение, предоставленное NinjaNye

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

Ваш запрос sql будет выглядеть аналогично (в зависимости от вашего ввода)

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]

Ответ 3

Попробуйте переместить order by после 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();

Ответ 4

Ваши требования повсюду, но это решение моего понимания:

Сгруппировать по свойству Reference:

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

Теперь вы говорите, что хотите ограничить результаты "самыми последними numOfEntries" - я полагаю, это означает, что вы хотите ограничить возвращаемые измерения... в этом случае:

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

Чтобы упорядочить группы по первому времени создания измерения (обратите внимание, что вы должны заказать измерения; если вы хотите получить самое раннее значение CreationTime, замените "g.SomeProperty" на "g.CreationTime" ):

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

Чтобы упорядочить группы по среднему CreationTime, используйте свойство Ticks структуры DateTime:

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