LINQ - ядро ​​Entity Framework - ВЫБРАТЬ самую последнюю запись, только если она соответствует другому условию - PullRequest
0 голосов
/ 16 марта 2020

У меня есть набор данных, который выглядит следующим образом. Я хочу выбрать все строки, в которых самая последняя запись для узла имеет статус «Ожидание»

Nodeid  Status     Type          Utctimestamp
------  -----      ------        --------   

1       Pending    Transaction   2020-03-13 03:31:00.00000+00
1       Overridden Transaction   2020-03-13 03:32:00.00000+00
2       Unknown    Other         2020-03-13 03:34:00.00000+00
2       Pending    Other         2020-03-13 03:35:00.00000+00

Запись для каждого узла должна быть возвращена, только если статус самой последней записи - Pending (иначе ничего не выбирайте для этого узла). Таким образом, для набора данных выше, запрос будет возвращать только следующее:

2       Pending    Other         2020-03-13 03:35:00.00000+00

Я хочу использовать запрос LINQ с Entity Framework, используя свободный синтаксис («Синтаксис метода»). Как мне это сделать?

Ответы [ 2 ]

0 голосов
/ 16 марта 2020

Я добился этого так:

var pending = myContext.Records
                         .GroupBy(t => t.Nodeid)
                         .Select(grp => grp.OrderByDescending(t => t.Utctimestamp).First())
                         .Where(t => t.Status == "Pending");
0 голосов
/ 16 марта 2020

Если вы хотите, чтобы последняя запись узла имела статус «Ожидание», и при условии, что у вас может быть узел, который переключился на «Ожидание» более одного раза: то есть

node ID  Status      Time
1        Pending     xxxx
1        Submitted   xxxy
1        Pending     xxxz

, где вы хотите строку для время "xxxz":

с EF6

string pendingStatus = Status.Pending.ToString();
var pending = myContext.TransactionMsg
    .Where(x => x.Status == pendingStatus)
    .GroupBy(x => x.NodeId)
    .Select(g => g.OrderByDescending(x => x.UtcTimestamp).FirstOrDefault())
    .ToList();

Теперь я не смог заставить это работать с EF Core (Сюрприз, сюрприз), так что если вы работаете с Core, вы ' Мне нужно будет немного покопаться, чтобы выяснить, как обойти, почему он не может выполнить OrderByDescending внутри выражения «Выбрать из группы». Еще одна причина придерживаться EF 6:)

Обновление: Хорошо, основано на требовании получить только узлы, на которых ожидается последний статус. Получить это в EF6 было относительно просто, переместив Where предложение после Select.

Выполнение этого в EF Core было намного сложнее, но выглядит возможным. Большая проблема в том, как GroupBy реализован в EF Core. Это намного более ограничено в отношении того, как вы можете взаимодействовать с результатами IGrouping, где EF6 будет успешно переводить / взаимодействовать с ними как набором, EF Core исключает попытку использовать OrderBy и такие выражения с сгруппированными результатами. Таким образом, вам нужно получить немного больше подробностей:

var results = context.Nodes
    .GroupBy(x => x.NodeId, (x,y) => new { Timestamp = y.Max(z => z.Timestamp), NodeId = x })
    .Join(context.Nodes, a => new { a.NodeId, a.Timestamp }, b => new { b.NodeId, b.Timestamp }, (a,b) => b)
    .Where(g => g.Status == "Pending")
    .ToList();

Это действительно выглядит странно, но быстро пройдемся: сначала мы группируем узлы по ID узла, а затем выбираем последнюю метку времени, используя Макс против сгруппированные результаты, чтобы получить наибольшую метку времени, а также выберите идентификатор узла с помощью сгруппированного ключа. Это дает нам структуру { Timestamp (max row), NodeId }. Используя это, мы Join возвращаемся к набору узлов, используя сравнение ключей по идентификатору узла и метке времени, чтобы связать выбранные результаты с коллекцией узлов, чтобы мы могли преобразовать эти сгруппированные элементы обратно в сущности узла. Это вернет нам коллекцию узлов, которые соответствуют Node ID и Max Timestamp, то есть «Последние узлы». Исходя из этого, мы применяем предложение Where, чтобы выбрать только последние узлы, которые находятся в состоянии «Ожидание».

...