【问题标题】:Multiple WHERE Clauses with LINQ extension methods具有 LINQ 扩展方法的多个 WHERE 子句
【发布时间】:2012-01-09 16:17:39
【问题描述】:

我有一个如下所示的 LINQ 查询:

DateTime today = DateTime.UtcNow;
var results = from order in context.Orders
              where ((order.OrderDate <= today) && (today <= order.OrderDate))
              select order;

我正在努力学习/理解 LINQ。在某些情况下,我需要添加两个额外的 WHERE 子句。为了做到这一点,我正在使用:

if (useAdditionalClauses)
{
  results = results.Where(o => o.OrderStatus == OrderStatus.Open)  // Now I'm stuck.
}

如您所见,我知道如何添加额外的 WHERE 子句。但是如何添加多个?例如,我想添加

WHERE o.OrderStatus == OrderStatus.Open AND o.CustomerID == customerID

我之前的查询。如何使用扩展方法做到这一点?

谢谢!

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    两种方式:

    results = results.Where(o => (o.OrderStatus == OrderStatus.Open) &&
                                 (o.CustomerID == customerID));
    

    或:

    results = results.Where(o => (o.OrderStatus == OrderStatus.Open))
                     .Where(o => (o.CustomerID == customerID));
    

    我通常更喜欢后者。但值得对 SQL 服务器进行分析以检查查询执行情况,看看哪一个对您的数据执行得更好(如果有任何差异的话)。

    关于链接.Where() 方法的说明:您可以将所需的所有LINQ 方法链接在一起。像.Where() 这样的方法实际上还没有针对数据库执行。他们defer execution 直到计算出实际结果(例如使用.Count().ToList())。因此,当您将多个方法链接在一起时(更多地调用.Where(),可能是.OrderBy() 或类似的东西,等等),它们会构建所谓的expression tree。这整个树是在评估数据源时针对数据源执行的。

    【讨论】:

    • 我觉得自己很愚蠢,不知道我能做到这一点。你刚刚把我从这么多意大利面条代码中拯救出来。
    • 谢谢,这对我有帮助。但是我是否也有可能根据某个变量触发任何一个 where 子句? @大卫
    • 你能在最后加上一个select子句吗?
    • @New_Coder:当然。 .Where() 子句不会改变返回类型。
    • 很奇怪,因为当我这样做时: List paths = db.ClientStatement_Inventory .Where(x => (x.statementYear == yea)) .Where(x => (x.statementMonth == mon)) .Select(c => c.statementPath).ToList();它不起作用。但如果我只有 1 个 where 子句,它会查询我的数据库。
    【解决方案2】:

    您可以像以前一样继续链接它们。

    results = results.Where (o => o.OrderStatus == OrderStatus.Open);
    results = results.Where (o => o.InvoicePaid);
    

    这表示 AND。

    【讨论】:

    • 你 - 和其他人 - 也打败了我,但这可能是最易读的方式。
    • 重复 where 子句被添加到查询中,中间有一个“and”运算符。
    • 这可能不是“最干净”的解决方案,但就我而言,它是迄今为止唯一有效的解决方案。我必须根据 UI 中的选择添加“where”子句。
    • 有没有办法让 Where's are "OR"ed?
    • 只有 1 个 where 与逻辑运算符如 And、Or 等。
    【解决方案3】:

    如果您使用内存数据(阅读“POCO 集合”),您还可以使用 PredicateBuilder 将表达式堆叠在一起,如下所示:

    // initial "false" condition just to start "OR" clause with
    var predicate = PredicateBuilder.False<YourDataClass>();
    
    if (condition1)
    {
        predicate = predicate.Or(d => d.SomeStringProperty == "Tom");
    }
    
    if (condition2)
    {
        predicate = predicate.Or(d => d.SomeStringProperty == "Alex");
    }
    
    if (condition3)
    {
        predicate = predicate.And(d => d.SomeIntProperty >= 4);
    }
    
    return originalCollection.Where<YourDataClass>(predicate.Compile());
    

    提到的PredicateBuilder 的完整来源如下(但您也可以通过更多示例查看original page):

    using System;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Collections.Generic;
    
    public static class PredicateBuilder
    {
      public static Expression<Func<T, bool>> True<T> ()  { return f => true;  }
      public static Expression<Func<T, bool>> False<T> () { return f => false; }
    
      public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                          Expression<Func<T, bool>> expr2)
      {
        var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
      }
    
      public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                           Expression<Func<T, bool>> expr2)
      {
        var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
      }
    }
    

    注意:我已经用Portable Class Library 项目测试了这种方法,并且必须使用.Compile() 才能使其工作:

    Where(谓词 .Compile() );

    【讨论】:

    • 这是否有理由不能与实体框架 LINQ 一起使用?
    • 对我来说也适用于 EF Core。生成的谓词被正确转换为 SQL。
    • 性能问题。即使是 EF Core 5,在使用上述 Predicate 时也无法编写优化的 SQL 查询。数据库中的所有表记录都将被提取到内存中,然后查询过滤器工作。我恢复到 query = query.Where 方法。
    【解决方案4】:

    当然:

    if (useAdditionalClauses) 
    { 
      results = 
        results.Where(o => o.OrderStatus == OrderStatus.Open && 
        o.CustomerID == customerID)  
    } 
    

    或者只是另一个像这样的.Where() 调用(虽然我不知道你为什么想要,除非它被另一个布尔控制变量分割):

    if (useAdditionalClauses) 
    { 
      results = results.Where(o => o.OrderStatus == OrderStatus.Open).
        Where(o => o.CustomerID == customerID);
    } 
    

    或者重新分配给results:`results = results.Where(blah)。

    【讨论】:

      【解决方案5】:

      您可以使用 && 并将所有条件写入同一个 where 子句,也可以使用 .Where().Where().Where()... 等等。

      【讨论】:

        【解决方案6】:
        results = context.Orders.Where(o => o.OrderDate <= today && today <= o.OrderDate)
        

        不需要选择,因为您已经在处理订单。

        【讨论】:

          【解决方案7】:

          只需使用 &amp;&amp; 运算符,就像使用任何其他需要执行布尔逻辑的语句一样。

          if (useAdditionalClauses)
          {
            results = results.Where(
                            o => o.OrderStatus == OrderStatus.Open 
                            && o.CustomerID == customerID)     
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-04-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-10-10
            • 1970-01-01
            • 1970-01-01
            • 2011-10-18
            相关资源
            最近更新 更多