【问题标题】:Linq: dynamic Where clause inside a nested subqueryLinq:嵌套子查询中的动态 Where 子句
【发布时间】:2013-10-09 23:19:00
【问题描述】:

过去,我通过向 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&lt;&gt;(如果使用 LinqToEntities,则为 or Expression&lt;Func&lt;&gt;&gt;),如下所示:

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&lt;&gt;,因为它包含对由查询的更高级别部分定义的变量 (customer) 的引用。我在这里错过了什么?

【问题讨论】:

    标签: c# linq predicatebuilder


    【解决方案1】:

    这可行:在单独的函数中构建谓词,将来自更高级别 lambda 的“客户”值作为参数传递。

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

    【讨论】:

      猜你喜欢
      • 2021-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多