实体框架 - Linq GroupBy来自多表的数据

时间:2018-03-09 13:26:39

标签: sql entity-framework linq group-by

我想通过Order.CustomerID对数据进行分组(使用Northwind作为示例)。

IN SQL看起来像这样:

Select
  o.CustomerID,
  SUM(od.Quantity * od.UnitPrice * (1-od.Discount))

FROM Orders o INNER JOIN [Order Details] od ON o.OrderID = od.OrderID
GROUP BY o.CustomerID

如何使用Linq加载该数据?

我试过

var order = context.Customers.Include("Orders.Order_Details")
            .SelectMany(x => x.Orders)
            .Select(x => new
            {
                Customer = x.Customer,
                Sum = x.Order_Details.Sum(y => (float)(y.Quantity * y.UnitPrice) * (1 - y.Discount))
            })
            .GroupBy(x => x.Customer)
            .Select(x => new
            {
                x.Key.CompanyName,
                Sum = x.Sum(y => y.Sum)
            });

但是,所产生的查询是丑陋而且太长。 任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

显然,客户和订单之间存在一对多的关系:每个客户都有零个或多个订单,每个订单只属于一个客户。

Orders和OrderDetails之间也存在一对多的关系:每个Order都有零个或多个OrderDetails,每个OrderDetail只属于一个Order。

此外,OrderDetail有Quantity,UnitPrice和Discount。

现在,根据您的SQL语句,您希望将所有订单分组到同一客户的订单组中。从您想要CustomerId的每个Group,以及该组中所有Orders的所有OrderDetails的价格总和。

使用LINQ可以使用多种方法。您试图从所有客户开始:

var totalOfAllOrdersOfCustomers = dbContext.Customers
    .Select(customer => new
    {
        CustomerId = customer.Id,
        TotalAllOrders = customer.Orders
            .SelectMany(order => orderDetails)
            .Select(orderDetail => orderDetail.Quantity * orderDetail.UnitPrice
                   * (1-orderDetail.Discount))
             .Sum();
    }

用语言表达:从每个客户那里获取Id。同时获取此客户的所有订单的所有OrderDetails(= SelectMany)。从每个OrderDetail使用公式计算价格,并将所有这些价格相加。

这将提供与您的SQL查询非常相似的内容。但是,存在差异:如果您的客户没有任何订单,您仍然可以获得CustomerId和Sum等于零的记录。

如果这是你想要的,你应该使用这种方法。

要获得没有任何订单的客户,SQL内部将使用Orders表从Customers表执行左外连接。通过查看订单表,您无法找到没有订单的客户。

您的SQL语句显示您不希望客户没有任何订单。如果是这种情况,您不必使用Customers表来获得结果:

var totalOfAllCustomersThatHaveOrders = dbContext.Orders
    .GroupBy(order => order.CustomerId)
    .Select(group => new
    {
        CustomerId = group.Key,
        TotalAllOrders = group.SelectMany(group.OrderDetails)
            .Select(orderDetail => orderDetail.Quantity * orderDetail.UnitPrice
                   * (1-orderDetail.Discount))
            .Sum();
    }

用语言表示:接受所有订单。将它们放入具有相同CustomerId(= GroupBy)的订单组中。从每个组中取Key(即组中所有订单的CustomerId)并将其分配给CustomerId。第二部分类似于第一个解决方案:从该组中的所有订单中获取所有OrderDetails(= SelectMany),并根据公式计算OrderDetail的价格。最后总结所有这些价格并将此总和分配给TotalOrders

因此,第一个需要额外的左外连接,因为它为您提供没有订单的客户。第二个更简单,但如果没有给你那些尚未订购的客户。

请注意,在您的最终结果中,您对客户的所有了解都是ID。如果您在此声明之后立即获取一些客户数据("哦,现在我已经获得了CustomerId,我喜欢客户名称和地址"),我' d使用第一种方法,从Customer表开始,然后选择所需的所有客户属性。这将为您节省一次数据库往返。

答案 1 :(得分:0)

我认为您不需要分组,因为所有实体都已链接:

context.Customers.Include("Orders.Order_Details").
    Select(c => new
    {
       Customer = c,
       Total = c.Orders.SelectMany(o => o. Order_Details).
                        Sum(d => d.Quantity * d.UnitPrice * (1-d.Discount))
    });