分组和左加入linq

Group by and left join in linq

有两个表,一个是 customers 有字段 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

通常,如果您发现自己在执行左外联接后跟 GroupBy,那是因为您想要 "items with their sub-items"、喜欢 "Schools with their Students"、"Clients with their Orders"、"CustomerGroups with their Customers"等。如果需要,请考虑使用 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,  // ???
    });

换言之:

获取 CustomerGroups 的序列。仅保留 属性 MerchantId 的值等于 merchantId 的那些 CustomerGroup。从每个剩余的 CustomerGroup 中,通过将 CustomerGroup.GroupId 与每个 Customer.GroupId.

进行比较,获取其所有客户

结果是一系列 CustomerGroups,每个都有其客户。从此结果(参数 ResultSelector)中获取来自客户的 GroupName 和来自该组中客户的数量。

您的陈述是:

Quantity = customers.CustomerID,

这行不通。我确定这不是您想要的。唉,你忘了写你想要的。我认为是这样的:

Quantity = customers.Count().

但是如果您想要此 CustomerGroup 中所有客户的 CustomerId:

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

如果需要,可以使用 ResultSelector 获取 "CustomerGroups with their Customers"。最有效的方法是 select 仅 select 您实际计划使用的属性:

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

不select客户外键的原因是效率。如果 CustomerGroup [14] 有 1000 个客户,则该组中的每个客户的 GroupId 值都等于 [14]。这个值[14]发送1001次就浪费了。