过去我通过动态地向Linq查询添加过滤器来处理可选搜索条件,如下所示:
public IEnumerable<Customer> FindCustomers(string name)
{
IEnumerable<Customer> customers = GetCustomers();
var results = customers.AsQueryable();
if (name != null)
{
results = results.Where(customer => customer.Name == name);
}
results = results.OrderBy(customer => customer.Name);
return results;
}
或者类似地使用谓词,你基本上只是将lambda从Where移动到Func<>
(如果使用LinqToEntities or Expression<Func<>>
),就像这样:
public IEnumerable<Customer> FindCustomers(string name)
{
Func<Customer, bool> searchPredicate = customer => true;
if (name != null)
{
searchPredicate = customer => customer.Name == name;
}
IEnumerable<Customer> customers = GetCustomers();
var results = customers
.Where(searchPredicate)
.OrderBy(customer => customer.Name);
return results;
}
但是,当Where子句隐藏在嵌套子查询中的某个地方时,我无法弄清楚如何做类似的事情。考虑以下(组成)场景:
public class Customer
{
public string Name;
public int MaxOrderItemAmount;
public ICollection<Order> PendingOrders;
public ICollection<OrderItem> FailedOrderItems;
public ICollection<Order> CompletedOrders;
}
public class Order
{
public int Id;
public ICollection<OrderItem> Items;
}
public class OrderItem
{
public int Amount;
}
public IEnumerable<OrderItem> FindInterestingOrderItems(
bool onlyIncludePendingItemsOverLimit)
{
var customers = GetCustomersWithOrders();
// This approach works, but yields an unnecessarily complex SQL
// query when onlyIncludePendingItemsOverLimit is false
var interestingOrderItems = customers
.SelectMany(customer => customer.PendingOrders
.SelectMany(order => order.Items
.Where(orderItem => onlyIncludePendingItemsOverLimit == false
|| orderItem.Amount > customer.MaxOrderItemAmount))
.Union(customer.FailedOrderItems)
);
// Instead I'd like to dynamically add the Where clause only if needed:
Func<OrderItem, bool> pendingOrderItemPredicate = orderItem => true;
if (onlyIncludePendingItemsOverLimit)
{
pendingOrderItemPredicate =
orderItem => orderItem.Amount > customer.MaxOrderItemAmount;
// PROBLEM: customer not defined here ^^^
}
interestingOrderItems = customers
.SelectMany(customer => customer.PendingOrders
.SelectMany(order => order.Items
.Where(pendingOrderItemPredicate)
.Union(customer.FailedOrderItems)))
.OrderByDescending(orderItem => orderItem.Amount);
return interestingOrderItems;
}
显然,这次我不能将lambda移动到Func<>
因为它包含对查询的更高级别部分定义的变量(customer
)的引用。我在这里缺少什么?
答案 0 :(得分:1)
这有效:在单独的函数中构建谓词,将来自更高级别lambda的“customer”值作为参数传递。
publicvoid FindInterestingOrderItems(bool onlyIncludePendingItemsOverLimit)
{
var customers = GetCustomersWithOrders();
var interestingOrderItems = customers
.SelectMany(customer => customer.PendingOrders
.SelectMany(order => order.Items
.Where(GetFilter(customer, onlyIncludePendingItemsOverLimit))
.Union(customer.FailedOrderItems)))
.OrderByDescending(orderItem => orderItem.Amount);
}
private Func<OrderItem, bool> GetFilter(Customer customer, bool onlyIncludePendingItemsOverLimit)
{
if (onlyIncludePendingItemsOverLimit)
{
return orderItem => orderItem.Amount > customer.MaxOrderItemAmount;
}
else
{
return orderItem => true;
}
}