如何从两个表达式创建表达式AND子句

时间:2012-10-02 03:04:32

标签: c# expression-trees

我正在尝试使用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.Func 2[WebApplication1.View_MyView,System.Boolean]' and 'System.Func 2 [WebApplication1.View_MyView,System.Boolean]'。

表达对我来说是新的,并且可能看了太多的代码,所以对如何去做这个有点困惑...如果你能指出我正确的方向,我将非常感激。

2 个答案:

答案 0 :(得分:5)

通过向BCL添加ExpressionVisitor,可以轻松重写表达式。有了一些助手,任务变得几乎无足轻重。

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

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之前,我们的库使用了该实现。

答案 1 :(得分: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<Func<Tab>>(...)调用给出的。在leftexp和rightexp中,有一些参数表达式对象必须与Expression.Lambda<Func<Tab>>(...)调用的对象匹配。

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

有关此问题的详情,请参阅here