我有一个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无法识别我.CalculateTotal()
实体上的LineItem.cs
方法。
答案 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);