8

我正在尝试使用 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.Func2[WebApplication1.View_MyView,System.Boolean]”定义。

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

4

2 回答 2

8

Rewriting expressions has been made easy with the addition of ExpressionVisitor to BCL. With some helpers the task gets almost trivial.

Here's a visitor class I use to apply a delegate to the tree nodes:

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

}

And here are the helper methods to simplify the rewriting:

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

}

Which allows to combine the expressions like this:

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

UPDATE:

In case ExpressionVisitor's not available, its source had been published a while ago here. Our library used that implementation until we've migrated to .NET 4.

于 2012-10-04T21:58:16.880 回答
1

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

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

它使用以下代码显示:

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>>(...)调用的对象相匹配。

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

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

于 2012-10-02T06:04:53.803 回答