【问题标题】:How to evaluate a standalone boolean expression in a LINQ expression tree如何评估 LINQ 表达式树中的独立布尔表达式
【发布时间】:2009-09-14 21:38:38
【问题描述】:

我正在使用标准访问者模式遍历 LINQ 表达式树,以生成动态 SQL WHERE 子句。

我的问题是,与 C# 不同,您不能在 SQL 中使用独立的布尔表达式;您必须将其与 1 或 0 进行比较。

给定这个假设的 lambda 表达式:

h => h.Enabled || h.Enabled == false

这个代码很容易产生错误:

WHERE Enabled OR Enabled = 0

或此代码:

WHERE (Enabled = 1) OR (Enabled = 1) = 0

两者当然都会产生 SQL 错误。当我深入研究子树以找出可能的情况时,我应该应用什么逻辑来解决这个问题,而不会让我的代码看起来真的很迟钝?

编辑:上面的例子当然是多余的——我只是用它来说明一个观点。

可以创建此场景的示例:

h => h.Enabled
h => h.Enabled == enabled
h => h.Enabled == true

当然,最后一个示例风格不佳,但我的代码被设计为独立于程序员的技能水平工作,因此不满足冗余场景对我来说是一种糟糕的形式。

【问题讨论】:

    标签: c# linq lambda expression-trees


    【解决方案1】:

    以下情况非常简单:

    h => h.Enabled == enabled
    h => h.Enabled == true
    

    这些是BinaryExpression节点,你可以直接翻译成:

    WHERE (Enabled = @p0)
    WHERE (Enabled = 1)
    

    您需要处理的特殊情况是:

    h => h.Enabled
    h => !h.Enabled
    

    它们在表达式树中的表示方式不同(如MemberExpression)。因此,您需要对MemberExpression 进行特殊处理,并确定它是否正在访问布尔属性。如果是,则将其转换为规范形式(在第二个示例中检测UnaryExpression):

    WHERE (Enabled = 1)
    WHERE (Enabled = 0)
    

    或者,您可以对表达式树进行预处理,并将任何特殊情况转换为其规范(表达式树)形式。例如,任何符合条件的MemberExpression 节点都可以转换为正确的BinaryExpression

    【讨论】:

    • 嗨,我正在努力实现同样的目标。你能帮我理解吗?例如,如果在一个表达式中我有 2 个不同的布尔表达式 x=>x.IsBool || x.IsBool == 星期二。如果我访问他们的 MemberExpressions,我会得到这个代码“WHERE IsBool = 1 OR IsBool = 1 = 1”,这显然是坏的。如何区分一个成员表达式有冗余部分而另一个是显式的?
    【解决方案2】:

    在评估运算符之前不可能完全处理操作数吗?

    即。评估每个:

    h => h.Enabled
    h => h.Enabled == enabled
    h => h.Enabled == true
    

    WHERE (Enabled = 1)
    

    然后在 lambda 中包含运算符的情况下,使用等效 SQL 处理呈现的操作数集合以满足运算符要求。

    【讨论】:

      【解决方案3】:

      其他人可能有同样的问题。我通过将最后一个 BinaryExpression 保存到变量中解决了这个问题 这将解决以下不同的布尔子句

      Where(f => f.IsActive == true || !f.IsActive == false || (f.Name == "adfd") && (f.IsActive || !f.IsBin) )

      这是处理独立布尔值的代码

      private string GetFilterMember(MemberExpression exp)
      {
          var str = exp.ToString();
          if (str.Count(f => f == '.') > 1)
          {
              var expType = exp.Type;
              var declareType = exp.Member.DeclaringType;
              if (declareType == typeof(string))
              {
                  if (exp.Member.Name == StringMethods.Length)
                  {
                      return $"{StringMethods.GetODataMethod(StringMethods.Length)}({exp.NameLeftPart()})";
                  }
              }
              else if (declareType == typeof(DateTime))
              {
      
              }
          }
          if (exp.Type == typeof(bool))
          {
              if (prviousBinaryExp != null && (prviousBinaryExp.Left == exp || prviousBinaryExp.Right == exp || (prviousBinaryExp.Left.NodeType == ExpressionType.Not && ((UnaryExpression)prviousBinaryExp.Left).Operand == exp) || (prviousBinaryExp.Right.NodeType == ExpressionType.Not && ((UnaryExpression)prviousBinaryExp.Right).Operand == exp)) && (prviousBinaryExp.NodeType != ExpressionType.Equal && prviousBinaryExp.NodeType != ExpressionType.NotEqual))
              {          
                  return $"{exp.Member.Name}{ODataQueryOperator.Equal}{FilterBuilder(Expression.Constant(true))}";
              }
          }
          return exp.Member.Name;
      }
      BinaryExpression prviousBinaryExp = null;
      protected virtual string FilterBuilder(Expression exp)
      {
      
          switch (exp.NodeType)
          {
              case ExpressionType.OrElse:
              case ExpressionType.AndAlso:
              case ExpressionType.Equal:
                  prviousBinaryExp = (BinaryExpression)exp;
                  return BinaryExpressionBuilder(((BinaryExpression)exp).Left, ODataQueryOperator.GetLogicalOperator(exp.NodeType), ((BinaryExpression)exp).Right);
              case ExpressionType.Constant:
                  return GetConstant(exp.Type, ((ConstantExpression)exp).Value);
              case ExpressionType.MemberAccess:
                  return GetFilterMember((MemberExpression)exp);
              case ExpressionType.Not:
                  return UnaryExpressionBuilder((UnaryExpression)exp);
              case ExpressionType.Call:
                  return MethodBuilder((MethodCallExpression)exp);
              default:
                  return string.Empty;
      
          }
      }
      

      不要关闭这个变量BinaryExpression prviousBinaryExp = null; 当你点击BinaryExpression 然后将该表达式保存在上面的变量中

      case ExpressionType.OrElse:
      case ExpressionType.AndAlso:
      case ExpressionType.Equal:
           prviousBinaryExp = (BinaryExpression)exp;
           return BinaryExpressionBuilder(((BinaryExpression)exp).Left, ODataQueryOperator.GetLogicalOperator(exp.NodeType), ((BinaryExpression)exp).Right);
      

      这是检查先前表达式是否为二进制表达式且当前表达式是否为 BinaryExpression's LeftRight 表达式的一部分的逻辑。我们将检查Nodtype 不应该是ExpressionType.EqualExpressionType.NotEqual(f.IsActive == true)。我们还将确保如果BinaryExpressionLeftRightUnaryExpression,我们将检查它的UnaryExpression.Operand 是否等于当前表达式。这将处理 !f.Enabled 或 !f.IsActive 等。

      最后,我将 Expression 转换为 OData 等效字符串。这是

              // converted String IsActive to OData equivalent
              //         IsActive          eq            true
              return $"{exp.Member.Name}{ODataQueryOperator.Equal}{FilterBuilder(Expression.Constant(true))}";
      

      【讨论】:

        猜你喜欢
        • 2013-12-15
        • 1970-01-01
        • 2017-02-08
        • 1970-01-01
        • 2017-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多