【问题标题】:How to create an Expression AND clause from two expressions如何从两个表达式创建表达式 AND 子句
【发布时间】:2012-10-02 03:04:31
【问题描述】:

我正在尝试使用 LINQ 为我的视图创建 where 子句。

我能够创建单列 where 子句,我现在想创建多列 where 子句..

我已经看到要在 .Net 4 及更高版本中实现的代码,但由于我必须使用 .Net 3.5,因此我需要快速解决这个问题。所以我想做的是....

 Expression leftexp = {tag=>((tag.id=2)||(tag.id=3))}
 Expression rightexp = {tag=>((tag.uid="MU")||(tag.uid="ST"))}

从这两个表达式我想创建

 BinaryExpression be = {tag=>((tag.id=2)||(tag.id=3))} && 
                       {tag=>((tag.uid="MU")||(tag.uid="ST"))} 

这样的东西我可以传递给我在 LINQ 中的 where 子句。

我尝试使用 Expression.And(leftexp,rightexp)

但得到了错误..

二元运算符 And 没有为类型定义
'System.Func2[WebApplication1.View_MyView,System.Boolean]' and 'System.Func2[WebApplication1.View_MyView,System.Boolean]'。

Expression 对我来说是新的,可能看了太多的代码,所以对如何去做这件事有点困惑......如果你能指出我正确的方向,我将不胜感激。

【问题讨论】:

  • 你不能连续调用Where() 两次,每个条件调用一次吗?我认为这可能比组合表达式更简单。
  • 是的,我想它会起作用,但我希望在 where 子句中运行 1-10 个不同的条件。所以我认为这种方法不实用。

标签: c# expression-trees


【解决方案1】:

通过将ExpressionVisitor 添加到 BCL,重写表达式变得很容易。有了一些助手,这项任务几乎变得微不足道。

这是我用来将委托应用于树节点的访问者类:

internal sealed class ExpressionDelegateVisitor : ExpressionVisitor {

    private readonly Func<Expression , Expression> m_Visitor;
    private readonly bool m_Recursive;

    public static Expression Visit ( Expression exp , Func<Expression , Expression> visitor , bool recursive ) {
        return new ExpressionDelegateVisitor ( visitor , recursive ).Visit ( exp );
    }

    private ExpressionDelegateVisitor ( Func<Expression , Expression> visitor , bool recursive ) {
        if ( visitor == null ) throw new ArgumentNullException ( nameof(visitor) );
        m_Visitor = visitor;
        m_Recursive = recursive;
    }

    public override Expression Visit ( Expression node ) {
        if ( m_Recursive ) {
            return base.Visit ( m_Visitor ( node ) );
        }
        else {
            var visited = m_Visitor ( node );
            if ( visited == node ) return base.Visit ( visited );
            return visited;
        }
    }

}

以下是简化重写的辅助方法:

public static class SystemLinqExpressionsExpressionExtensions {

    public static Expression Visit ( this Expression self , Func<Expression , Expression> visitor , bool recursive = false ) {
        return ExpressionDelegateVisitor.Visit ( self , visitor , recursive );
    }

    public static Expression Replace ( this Expression self , Expression source , Expression target ) {
        return self.Visit ( x => x == source ? target : x );
    }

    public static Expression<Func<T , bool>> CombineAnd<T> ( this Expression<Func<T , bool>> self , Expression<Func<T , bool>> other ) {
        var parameter = Expression.Parameter ( typeof ( T ) , "a" );
        return Expression.Lambda<Func<T , bool>> (
            Expression.AndAlso (
                self.Body.Replace ( self.Parameters[0] , parameter ) ,
                other.Body.Replace ( other.Parameters[0] , parameter )
            ) ,
            parameter
        );
    }

}

允许像这样组合表达式:

static void Main () {
    Expression<Func<int , bool>> leftExp = a => a > 3;
    Expression<Func<int , bool>> rightExp = a => a < 7;
    var andExp = leftExp.CombineAnd ( rightExp );
}

更新:

如果ExpressionVisitor 不可用,则其来源已在不久前发布here。在我们迁移到 .NET 4 之前,我们的库一直使用该实现。

【讨论】:

    【解决方案2】:

    如果不将两个完整的表达式树都重写为一个完整的新树,您就无法做到这一点。

    原因:整个表达式树的参数表达式对象必须相同。如果将两者结合起来,则同一参数有两个参数表达式对象,这是行不通的。

    显示如下代码:

    Expression<Func<Tab, bool>> leftexp = tag => ((tag.id == 2) || (tag.id == 3));
    Expression<Func<Tab, bool>> rightexp = tag => ((tag.uid == "MU") || (tag.uid == "ST"));
    
    Expression binaryexp = Expression.AndAlso(leftexp.Body, rightexp.Body);
    ParameterExpression[] parameters = new ParameterExpression[1] {
        Expression.Parameter(typeof(Tab), leftexp.Parameters.First().Name)
    };
    Expression<Func<Tab, bool>> lambdaExp = Expression.Lambda<Func<Tab, bool>>(binaryexp, parameters);
    
    var lambda = lambdaExp.Compile();
    

    这在 lambdaExp.Compile() 调用上失败,并给出以下异常:

    Lambda Parameter not in scope
    

    这是因为我基本上是在重复使用 leftexp 和 rightexp 表达式,但它们有不同的参数表达式,这两者都不是我在Expression.Lambda&lt;Func&lt;Tab&gt;&gt;(...) 调用中给出的。在 leftexp 和 rightexp 的深处,有参数表达式对象必须与Expression.Lambda&lt;Func&lt;Tab&gt;&gt;(...) 调用的对象相匹配。

    要解决这个问题,您必须使用新的(单个)参数表达式为参数标记重新创建完整的表达式。

    有关该问题的更多信息,请参阅here

    【讨论】:

    • 那么,您将如何创建新表达式?
    • 对于我的示例,我认为我将不得不部分地构造表达式,这是我能想到的使用相同参数表达式的唯一方法。我认为没有直接的方法可以将参数表达式传递给表达式。
    • @svick,我已经对表达式树进行了一些重写,但还有其他目的。可以在此处找到一个示例:pastebin.com/KyhwKHBJ,我用它来将 Expression> 转换为 Expression,其中 T1 和 T2 具有相似的属性。警告:此代码并不“完整”,因为并非支持 lambda 表达式中的所有可能性。以此为起点。
    猜你喜欢
    • 2011-12-13
    • 1970-01-01
    • 1970-01-01
    • 2014-06-16
    • 2010-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-29
    相关资源
    最近更新 更多