Pyr*_*kee 5 c# expression expression-trees
我需要连接两个表达式(withor语句)
我的代码:
var items = new List<Item>
{
new Item { Color = "Black", Categories = new List<string> { "cat1", "cat2" } },
new Item { Color = "Red", Categories = new List<string> { "cat3" } },
new Item { Color = "White", Categories = new List<string> { "cat1" } }
};
var categories = new List<string> { "cat2", "cat3" };
Expression<Func<Item, bool>> func1 = (x1) => x1.Color == "Black";
Expression<Func<Item, bool>> func2 = (x2) => x2.Categories.Any(y => categories.Where(z => z == y).Any());
Expression<Func<Item, bool>> fullExpression = Expression.Lambda<Func<Item, bool>>(
Expression.Or(func1.Body, func2.Body), func1.Parameters.Single());
var result = items.AsQueryable().Where(fullExpression);
// result should be like this
// items.Where(x => (x.Color == "Black") || x.Categories.Any(y => categories.Where(z => z == y).Any()))
Run Code Online (Sandbox Code Playgroud)
我收到运行时错误variable 'x2' of type 'Item' referenced from scope '', but it is not defined'
我还尝试使用 ExpressionVisitor 构建表达式。
这是ExpressionVisitor:
internal class ParameterReplacer : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
internal ParameterReplacer(ParameterExpression parameter)
{
_parameter = parameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return base.VisitParameter(_parameter);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我在代码中使用它的方法:
Expression<Func<Item, bool>> func1 = (x1) => x1.Color == "Black";
Expression<Func<Item, bool>> func2 = (x2) => x2.Categories.Any(y => categories.Select(z => z == y).Any());
var paramExpr = Expression.Parameter(typeof(Item));
var exprBody = Expression.Or(func1.Body, func2.Body);
exprBody = (BinaryExpression)new ParameterReplacer(paramExpr).Visit(exprBody);
var finalExpr = Expression.Lambda<Func<Item, bool>>(exprBody, paramExpr);
var result = items.AsQueryable().Where(finalExpr);
Run Code Online (Sandbox Code Playgroud)
在这种情况下,在创建过程中ParameterReplacer我收到错误
System.InvalidOperationException: 'The operands for operator 'Equal' do not match the parameters of method 'op_Equality'.'
Run Code Online (Sandbox Code Playgroud)
我做错了什么?
Ian Newson 是完全正确的,但如果你想要代码,就在这里:)
使用这两个类,您可以组合两个谓词。我没有想出它,但对其进行了一些改进/调整,并使其使用该类型,Predicate而不是 Func与一些较新的语言功能一起使用(原始版本相当旧,遗憾的是我不记得在哪里找到它)。
internal class SubstExpressionVisitor : ExpressionVisitor
{
private readonly Dictionary<Expression, Expression> _subst = new Dictionary<Expression, Expression>();
protected override Expression VisitParameter(ParameterExpression node)
{
if (_subst.TryGetValue(node, out Expression newValue))
{
return newValue;
}
return node;
}
public Expression this[Expression original]
{
get => _subst[original];
set => _subst[original] = value;
}
}
Run Code Online (Sandbox Code Playgroud)
public static class PredicateBuilder
{
// you don't seem to need this but it's included for completeness sake
public static Expression<Predicate<T>> And<T>(this Expression<Predicate<T>> a, Expression<Predicate<T>> b)
{
if (a == null)
throw new ArgumentNullException(nameof(a));
if (b == null)
throw new ArgumentNullException(nameof(b));
ParameterExpression p = a.Parameters[0];
SubstExpressionVisitor visitor = new SubstExpressionVisitor();
visitor[b.Parameters[0]] = p;
Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body));
return Expression.Lambda<Predicate<T>>(body, p);
}
public static Expression<Predicate<T>> Or<T>(this Expression<Predicate<T>> a, Expression<Predicate<T>> b)
{
if (a == null)
throw new ArgumentNullException(nameof(a));
if (b == null)
throw new ArgumentNullException(nameof(b));
ParameterExpression p = a.Parameters[0];
SubstExpressionVisitor visitor = new SubstExpressionVisitor();
visitor[b.Parameters[0]] = p;
Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body));
return Expression.Lambda<Predicate<T>>(body, p);
}
}
Run Code Online (Sandbox Code Playgroud)
你可以这样使用它:
Expression<Predicate<Item>> func1 = (x1) => x1.Color == "Black";
Expression<Predicate<Item>> func2 = (x2) => x2.Categories.Any(y => categories.Select(z => z == y).Any());
Expression<Predicate<Item>> finalExpr = func1.Or(func2);
Run Code Online (Sandbox Code Playgroud)
您可能需要记住, myOr正在内部使用,因此如果前面的表达式被评估为 true (类似于, not ) OrElse,则不会评估第二个表达式。( is like , not )也是如此。
另一件需要注意的事情是,如果由于某种原因必须使用,您可以轻松替换为:)OrElse|||AndAndAlsoAndAlso&&&Predicate<T>Func<T, bool>Func