Группа Entity Framework по запросу левого соединения - PullRequest
1 голос
/ 17 июня 2020

Результат должен выглядеть так: result should look like this

Я начал использовать Entity Framework в течение одного месяца, поэтому я не знаком с запросами linq. Запрос, который я написал в SQL:

SELECT 
    om0001.CUSTOMER, om0001.ITEM_CODE, 
    SUM(om0001.AMOUNT) AS AMOUNT, 
    SUM(ep0001.EXPORT_AMOUNT) AS EXPORT_AMOUNT
FROM
    om0001
LEFT OUTER JOIN 
    ep0001 ON om0001.ID = ep0001.om0001_ID
GROUP BY 
    om0001.CUSTOMER, om0001.ITEM_CODE; 

Когда я запускаю этот запрос в SQL, он работает хорошо, поэтому я попытался преобразовать его в запросы linq.

Что я сделано до сих пор:

var testjoin = from om0001 in EF.om0001
               join ep0001 in EF.ep0001 on om0001.ID equals ep0001.om0001_ID
               into jointable
               from z in jointable.DefaultIfEmpty()
               group z by new {om0001.CUSTOMER, om0001.ITEM_CODE } into g
               select new
                      {
                          CUSTOMER = g.Key.CUSTOMER,
                          ITEM_CODE = g.Key.ITEM_CODE,
                          om0001SUMamount = g.Sum(x => x.AMOUNT),
                          ep0001EXPORTsumAmount = g.Sum(y => y.EXPORT_AMOUNT)
                      };

Проблема с этим запросом linq заключается в том, что я не могу получить om0001SUMamount. Я получаю только данные столбца ep0001. Пожалуйста, помогите

Ответы [ 2 ]

1 голос
/ 17 июня 2020

Очевидно, я не могу заглянуть в вашу базу данных EF, поэтому я создал несколько образцов данных (подразумеваются структуры классов 'item'):

var EF = new efClass {
    om0001 = new List<om0001item> {
        new om0001item { ID = 0, CUSTOMER = 0, ITEM_CODE = 0, AMOUNT = 10 },
        new om0001item { ID = 1, CUSTOMER = 0, ITEM_CODE = 0, AMOUNT = 20 },
        new om0001item { ID = 2, CUSTOMER = 1, ITEM_CODE = 1, AMOUNT = 30 },
        new om0001item { ID = 3, CUSTOMER = 1, ITEM_CODE = 1, AMOUNT = 40 }
    },
    ep0001 = new List<ep0001item> {
        new ep0001item { om0001_ID = 0, EXPORT_AMOUNT = -20 },
        new ep0001item { om0001_ID = 1, EXPORT_AMOUNT = -20 }        
    }
};

С этими данными я создал запрос, который, честно говоря, кажется Неэлегантно и разочаровало меня, но такова природа левых объединений в LINQ:

var testjoin = from om0001 in EF.om0001
    join ep0001 in EF.ep0001 on om0001.ID equals ep0001.om0001_ID into jointable
    select new { om0001, ep0001 = jointable.DefaultIfEmpty() } into combined
    group combined by new { 
        combined.om0001.CUSTOMER,
        combined.om0001.ITEM_CODE
    } into g
    select new {
       CUSTOMER = g.Key.CUSTOMER,
       ITEM_CODE = g.Key.ITEM_CODE,
       om0001SUMamount = g.Sum(x => x.om0001.AMOUNT),
       ep0001EXPORTsumAmount = g.Sum(x => x?.ep0001.Sum(y => y?.EXPORT_AMOUNT ?? 0)) 
    };

Суть в том, что когда вы группируете по «объединяемому», вы теряете ссылку на ep0001. Итак, выделите и ep0001, и om0001 в новый «комбинированный» объект, а затем сгруппируйте его на основе этого.

Когда я создавал библиотеку javascript ( fluent-data ), которая имела некоторые LINQ-подобные функции, я испытывал большое уважение и сострадание к разработчикам LINQ. Тем не менее, я не знаю, почему они просто не создают оператор левого соединения, чтобы повысить ценность всех разработчиков C#, использующих LINQ.

0 голосов
/ 17 июня 2020

Итак, у вас есть таблица с Oms (на самом деле Om00001, но я не собираюсь писать все эти 0001 снова и снова) и таблица с Eps, и у вас есть один-ко-многим отношение между Oms и Eps: каждый Om имеет ноль или более Eps, и каждый Ep принадлежит ровно одному Om, а именно Om, на который ссылается внешний ключ EpId.

Если вы следовали соглашениям о первом коде Entity Framework , у вас будут классы, похожие на следующие:

class Om
{
    public int Id {get; set;}
    public string Customer {get; set;}
    public string ItemCode {get; set;}
    ...

    // Every Om has zero or more Eps (one-to-many)
    public virtual ICollection<Ep> Eps {get; set;}
}

class Ep
{
    public int Id {get; set;}
    public int Amount {get; set;}
    public int ExportAmount {get; set;}
    ...

    // every Ep belongs to exactly one Om, using foreign key
    public int OmId {get; set;}
    public virtual Om Om {get; set;}
}

Этого достаточно для Entity Framework, чтобы определить ваш индивидуальный подход. -многие отношения. Поскольку я следовал соглашениям, мне не нужны ни атрибуты, ни свободный API. Если вам нужны разные имена таблиц или столбцов, вам нужен свободный API / атрибуты.

В структуре сущностей невиртуальные свойства представляют столбцы ваших таблиц, виртуальные свойства представляют отношения между таблицами (один-ко-многим, многие-ко-многим, ...)

Решение с использованием GroupJoin

var result = dbContext.Oms.GroupJoin(dbContext.Eps,
    om => om.Id,                   // from every Om take the primary key
    ep => ep.OmId,                 // from every ep take the foreign key to the Om

    (om, epsOfThisOm) => new       // from every om and all eps having the correct foreign key
    {                              // make one new object
        // Select only the properties that you plan to use:
        Customer = om.Customer,
        ItemCode = om.ItemCode,

        Amount = epsOfThisOm.Sum(ep => ep.Amount);
        ExportAmount = epsOfThisOm.Sum(ep => ep.ExportAmount);
    });

Решение с использованием виртуальной ICollection

Вместо для выполнения GroupJoin вы также можете использовать виртуальную коллекцию ICollection.

Требование : из каждого Ом, дайте мне клиента и ItemCode, а также сумму всех сумм его Eps, и сумма всех ExportAmounts его Eps.

var result = dbContext.Oms.Select(om => new
{
    Customer = om.Customer,
    ItemCode = om.ItemCode,

    Amount = om.Eps.Sum(ep => ep.Amount);
    ExportAmount = om.Eps.Sum(ep => ep.ExportAmount);
});        

Это выглядит намного аккуратнее и более точно соответствует вашим требованиям. Entity framework знает отношения и сделает для вас правильный GroupJoin.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...