Как присоединиться к вложенному запросу с помощью LINQ

#linq #.net-core #entity-framework-core

#linq #.net-core #entity-framework-core

Вопрос:

Я новичок в Entity Framework и LINQ и изо всех сил пытаюсь «преобразовать» свой SQL-запрос в его синтаксис.

Приведена таблица, содержащая производственные заказы с OrderNumber , Quantity Material и т.д. в качестве столбцов. Кроме того, есть столбец Timestamp .

Если количество заказа будет обновлено, это приведет к появлению совершенно новой записи в таблице, где все значения будут такими же, как в предыдущей строке, за исключением Timestamp (указывающего время изменения) и Quantity (содержащего новое значение). Это позволяет мне иметь «историю» заказа.

Выбор только последней версии определенного заказа с использованием SQL может выглядеть следующим образом:

 SELECT p1.*
FROM dbo.ProductionOrders as p1
  JOIN (SELECT OrderNumber, MAX(Timestamp) as Timestamp FROM dbo.ProductionOrders GROUP BY OrderNumber) as p2
    on p1.OrderNumber = p2.OrderNumber and p1.Timestamp = p2.Timestamp
WHERE p1.OrderNumber = 'order-182736'
  

Та же функциональность, которую я реализовал в своем коде на C #, выглядит следующим образом:

 var productionOrder = _db.ProductionOrders.Where(po =&&t; po.OrderNumber == "order-182736")
                                          .OrderByDescendin&(po =&&t; po.Timestamp)
                                          .FirstOrDefault();
  

Я теряюсь, когда хочу получить несколько строк. Например, все заказы на определенный материал. Используя SQL, мне нужно только настроить WHERE условие:

 SELECT p1.*
FROM dbo.ProductionOrders as p1
  JOIN (SELECT OrderNumber, MAX(Timestamp) as Timestamp FROM dbo.ProductionOrders GROUP BY OrderNumber) as p2
    on p1.OrderNumber = p2.OrderNumber and p1.Timestamp = p2.Timestamp
WHERE p1.Material = 42
  

Примеры, которые я нашел для JOIN использования LINQ, не работают с вложенными запросами (мой основной источник microsoft.com)

Как я могу получить все последние «версии» заказов на определенный материал с помощью LINQ?

Ответ №1:

Вы можете сделать это точно так же, как в SQL. _db.ProductionOrders в LINQ к сущностям представляет dbo.ProductionOrders в SQL, и вы можете использовать его как на верхнем уровне, так и на уровне подзапроса:

 var query = _db.ProductionOrders
    .Join(_db.ProductionOrders
        .GroupBy(p2 =&&t; new { p2.OrderNumber })
        .Select(& =&&t; new { &.Key.OrderNumber, Timestamp = &.Max(e =&&t; e.Timestamp) }), // subquery
        p1 =&&t; new { p1.OrderNumber, p1.Timestamp }, p2 =&&t; p2, // join condition
        (p1, p2) =&&t; p1) // result selector (p1.*)
    .Where(p1 =&&t; p1.Material == 42);
  

(Примечание: не уверен, откуда Label берется информация в ваших SQL-запросах, поэтому использую OrderNumber вместо этого).

или, если вы предпочитаете, то же самое в синтаксисе запроса LINQ:

 var query =
    from p1 in _db.ProductionOrders
    join p2 in (from p2 in _db.ProductionOrders
                &roup p2 by new { p2.OrderNumber }) into &
                select new { &.Key.OrderNumber, Timestamp = &.Max(p2 =&&t; p2.Timestamp) })
    on new { p1.OrderNumber, p1.Timestamp } equals p2
    where p1.Material == 42
    select p1;
  

Комментарии:

1. Label все еще был там из-за некоторых ошибок копирования и вставки, которые я допустил, я изменил OP. Мне нужно некоторое время, чтобы разобраться в LINQ…