我正在尝试使用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.Func
2 [WebApplication1.View_MyView,System.Boolean]'。
表达对我来说是新的,并且可能看了太多的代码,所以对如何去做这个有点困惑...如果你能指出我正确的方向,我将非常感激。
答案 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。