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

AJ1*_*J17 8 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"))}
Run Code Online (Sandbox Code Playgroud)

从我想创建的这两个表达式

 BinaryExpression be = {tag=>((tag.id=2)||(tag.id=3))} && 
                       {tag=>((tag.uid="MU")||(tag.uid="ST"))} 
Run Code Online (Sandbox Code Playgroud)

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

我试着用Expression.And(leftexp,rightexp)

但得到了错误..

二进制运算符And没有为类型
System.Func 2[WebApplication1.View_MyView,System.Boolean]' and 'System.Func2 [WebApplication1.View_MyView,System.Boolean]'定义.

表达对我来说是新的,可能已经看了太多的代码,所以有点混淆如何去做...如果你能指出我正确的方向,真的很感激.

cha*_*ase 6

通过将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;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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

}
Run Code Online (Sandbox Code Playgroud)

这允许组合这样的表达式:

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

更新:

在情况下ExpressionVisitor的不可用,它的来源已经出版前一段时间在这里.我们的库使用了该实现,直到我们迁移到.NET 4.


Maa*_*ten 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();
Run Code Online (Sandbox Code Playgroud)

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

Lambda Parameter not in scope
Run Code Online (Sandbox Code Playgroud)

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

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

有关该问题的更多信息,请参阅此处。

  • 那么,您究竟将如何创建这个新表达式呢? (3认同)