如何根据订单价值获得前10名客户

时间:2017-07-28 07:36:43

标签: linq

我有一个Customers列表,每个列表都有Orders列表。每个Order都有一个LineItems列表。

我想编写一个LINQ查询,根据订单价值(即花费的钱)而不是订单总数,我会得到前10名客户。

一个客户可能有2个订单但可能花费10,000英镑,但另一个客户可能有100个订单,而且只花了500英镑。

现在,我已根据订单数量获得前10名客户。

var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
        let activeCount = c.SaleOrders.Count(so => so.Status != SaleOrderStatus.Cancelled)
        orderby activeCount descending
        select c).Take(10);

更新

感谢Jon Skeet关于做一个双Sum的评论,我编写了以下编译的查询。

var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
        let orderSum = c.SaleOrders.Where(so => so.Status != SaleOrderStatus.Cancelled)
                                   .Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))
        orderby orderSum descending
        select c).Take(10);

但是当我运行它时,我收到以下错误:

LINQ doesn't recognise method on entity

似乎LINQ无法识别我.CalculateTotal()实体上的LineItem.cs方法。

1 个答案:

答案 0 :(得分:1)

您遇到的问题是CalculateTotal()不是Linq可以转换为SQL的东西(在运行时完成,因此没有编译器错误)。

这里的基本问题是Linq并没有真正使用lambdas(Func<>),而是实际上是表达式(Expression<Func<>>),这是部分编译状态下的代码,Linq然后去了关于反汇编和转换为SQL。

因此,假设CalculateTotal是一个定义如下的成员函数:

 public decimal CalculateTotal()
 {
      return this.quantity * this.value;
 }

我们可以将其定义为本地lambda函数

 Func<LineItem, decimal> CalculateTotal = (li => li.quantity * li.value);

现在,我们有一个lambda,它接受一个LineItem并返回一个值,这正是Sum()想要的,所以现在我们可以替换:

.Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))

.Sum(so => so.LineItems.Sum(CalculateTotal))

但是,就像之前一样,这会崩溃,因为正如我所说,它需要一个表达式。所以,我们给它一个:

 Expression<Func<LineItem, decimal>> CalculateTotal = (li => li.quantity * li.value);