Сгруппировать и оставить присоединиться в linq - PullRequest
0 голосов
/ 07 апреля 2020

Есть две таблицы, одна для клиентов имеет поля customerID, GroupID, а другая CustomerGroup имеет поля GroupID, GroupName, я хочу получить количество customerID в каждой группе есть оператор LINQ:

var groups = from customerGroups in db.CustomerGroup 
                         join customers in db.Customers on customerGroups.GroupID equals customers.GroupID into gc
                         where customerGroups.MerchantID == merchantID
                         from subCustomerGroups in gc.DefaultIfEmpty()
                         group customerGroups by customerGroups.GroupName into grpCustomerGroups
                         select new { GroupName = grpCustomerGroups.Key, Quantity = customers.Count()};

проблема в том, что Quantity = customers.Count() неверен, как исправить оператор? Ожидаемое значение sql составляет

exec sp_executesql N'SELECT 
    1 AS [C1], 
    [GroupBy1].[K1] AS [GroupName], 
    [GroupBy1].[A1] AS [C2]
    FROM ( SELECT 
        [Extent1].[GroupName] AS [K1], 
        COUNT(CustomerID) AS [A1]
        FROM  [dbo].[CustomerGroup] AS [Extent1]
        LEFT OUTER JOIN [dbo].[Customer] AS [Extent2] ON [Extent1].[GroupID] = [Extent2].[GroupID]
        WHERE [Extent1].[MerchantID] = @p__linq__0
        GROUP BY [Extent1].[GroupName]
    )  AS [GroupBy1]',N'@p__linq__0 bigint',@p__linq__0=9

1 Ответ

1 голос
/ 07 апреля 2020

Обычно, если вы обнаруживаете, что выполняете внешнее левое соединение, за которым следует GroupBy, это потому, что вам нужны «элементы со своими подпунктами», например «Школы со своими учениками», «Клиенты со своими заказами», «Группы клиентов» со своими клиентами ", et c. Если вы хотите этого, рассмотрите возможность использования GroupJoin вместо «Join + DefaultIfEmpty + GroupBy»

Я более знаком с синтаксисом метода, поэтому я буду его использовать.

int merchantId = ...
var result = dbContext.CustomerGroups

    // keep only the CustomerGroups from merchantId
    .Where(customerGroup => customerGroup.MerchantId == merchantId)

    .GroupJoin(dbContext.Customers,            // GroupJoin with Customers
    customerGroup => customerGroup.GroupId,    // from every CustomerGroup take the GroupId
    customer => customer.GroupId,              // from every Customer take the GroupId

    // ResultSelector:
    (customerGroup, customersInThisGroup) => new  // from every CustomerGroup with all its
    {                                             // matching customers make one new object
        GroupName = customerGroup.Key,
        Quantity = customersInThisGroup.CustomerId,  // ???
    });

Словами:

Взять последовательность групп клиентов. Оставьте только те группы клиентов, которые имеют значение свойства MerchantId равным merchantId. Из каждой оставшейся CustomerGroup получите всех своих клиентов, сравнив CustomerGroup.GroupId с каждым Customer.GroupId.

Результатом является последовательность CustomerGroups, каждая со своими клиентами. Из этого результата (параметр ResultSelector) получите GroupName от клиента и количество от клиентов в этой группе.

Ваше утверждение было:

Quantity = customers.CustomerID,

Это не будет работать. Я уверен, что это не то, что вы хотите. Увы, ты забыл написать что хочешь. Я думаю, что это:

Quantity = customers.Count().

Но если вы хотите, чтобы CustomerId всех клиентов в этой группе клиентов:

// ResultSelector:
(customerGroup, customersInThisGroup) => new
{                                           
    GroupName = customerGroup.Key,
    CustomerIds = customersInThisGroup.Select(customer => customer.CustomerId)
                                      .ToList(),
);

Если вы хотите, вы можете использовать ResultSelector, чтобы получить "Группы клиентов с их клиенты ". Наиболее эффективным является выбор только тех свойств, которые вы фактически планируете использовать:

// ResultSelector:
(customerGroup, customersInThisGroup) => new
{      
    // select only the CustomerGroup properties that you plan to use:
    Id = CustomerGroup.GroupId,
    Name = CustomerGroup.Name,
    ... // other properties that you plan to use

    Customers = customersInThisGroup.Select(customer => new
    {
         // again, select only the Customer properties that you plan to use
         Id = customer.Id,
         Name = customer.Name,
         ...

         // not needed, you know the value:
         // GroupId = customer.GroupId
    });

Причина, по которой вы не выбираете внешний ключ Клиентов, - это эффективность. Если CustomerGroup [14] имеет 1000 клиентов, то для каждого клиента в этой группе значение GroupId будет равно [14]. Было бы напрасно посылать это значение [14] 1001 раз.

...