问题描述
我有两个 Expression<Func<T, bool>> 类型的表达式,我想对这些表达式进行 OR、AND 或 NOT,并获得一个相同类型的新表达式
p>
I have two expressions of type Expression<Func<T, bool>>
and I want to take to OR, AND or NOT of these and get a new expression of the same type
Expression<Func<T, bool>> expr1;
Expression<Func<T, bool>> expr2;
...
//how to do this (the code below will obviously not work)
Expression<Func<T, bool>> andExpression = expr AND expr2
推荐答案
嗯,可以用Expression.AndAlso
/OrElse
等组合逻辑表达式,但是问题出在是参数;你在 expr1 和 expr2 中使用相同的 ParameterExpression
吗?如果是这样,那就更容易了:
Well, you can use Expression.AndAlso
/ OrElse
etc to combine logical expressions, but the problem is the parameters; are you working with the same ParameterExpression
in expr1 and expr2? If so, it is easier:
var body = Expression.AndAlso(expr1.Body, expr2.Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, expr1.Parameters[0]);
这也适用于否定单个操作:
This also works well to negate a single operation:
static Expression<Func<T, bool>> Not<T>(
this Expression<Func<T, bool>> expr)
{
return Expression.Lambda<Func<T, bool>>(
Expression.Not(expr.Body), expr.Parameters[0]);
}
否则,根据 LINQ 提供程序,您或许可以将它们与 Invoke
结合使用:
Otherwise, depending on the LINQ provider, you might be able to combine them with Invoke
:
// OrElse is very similar...
static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> left,
Expression<Func<T, bool>> right)
{
var param = Expression.Parameter(typeof(T), "x");
var body = Expression.AndAlso(
Expression.Invoke(left, param),
Expression.Invoke(right, param)
);
var lambda = Expression.Lambda<Func<T, bool>>(body, param);
return lambda;
}
在某处,我有一些代码重写了表达式树替换节点以消除对 Invoke
的需要,但它相当冗长(我不记得我把它放在哪里了...)
Somewhere, I have got some code that re-writes an expression-tree replacing nodes to remove the need for Invoke
, but it is quite lengthy (and I can't remember where I left it...)
选择最简单路线的通用版本:
Generalized version that picks the simplest route:
static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
// need to detect whether they use the same
// parameter instance; if not, they need fixing
ParameterExpression param = expr1.Parameters[0];
if (ReferenceEquals(param, expr2.Parameters[0]))
{
// simple version
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(expr1.Body, expr2.Body), param);
}
// otherwise, keep expr1 "as is" and invoke expr2
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(
expr1.Body,
Expression.Invoke(expr2, param)), param);
}
从 .NET 4.0 开始,ExpressionVisitor
类允许您构建 EF 安全的表达式.
Starting from .NET 4.0, there is the ExpressionVisitor
class which allows you to build expressions that are EF safe.
public static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof (T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
这篇关于组合两个表达式 (Expression<Func<T, bool>>)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!