为了账号安全,请及时绑定邮箱和手机立即绑定

组合两个表达式(表达式<Func<T,bool>)

组合两个表达式(表达式<Func<T,bool>)

C#
守着星空守着你 2019-06-19 11:04:55
组合两个表达式(表达式<Func<T,bool>)我有两个类型的表达式Expression<Func<T, bool>>我想要得到或,或者不是这些,得到一个相同类型的新表达式。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
查看完整描述

3 回答

?
墨色风雨

TA贡献1853条经验 获得超6个赞

你可以用Expression.AndAlso / OrElse等等来组合逻辑表达式,但问题在于参数;您是否使用相同的ParameterExpression在expr1和expr2中?如果是这样,则更容易:

var body = Expression.AndAlso(expr1.Body, expr2.Body);var lambda = Expression.Lambda<Func<T,bool>>(body, expr1.Parameters[0]);

这对于否定单个操作也很有效:

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:

// 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,但它很长(我不记得我把它放在哪里了.)


选择最简单路径的通用版本:

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开始。有ExpressionVistor类,它允许您构建EF安全的表达式。

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


查看完整回答
反对 回复 2019-06-19
?
Qyouu

TA贡献1786条经验 获得超11个赞

您可以使用Expression.AndAlso/OrElse组合逻辑表达式,但必须确保参数表达式是相同的。

我在EF和预测生成器所以我做了自己的事而不诉诸于调用,所以我可以像这样使用:

var filterC = filterA.And(filterb);

我的预测生成器的源代码:

public static class PredicateBuilder {

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }   }

以及替换lambda中的参数的实用程序类:

internal class SubstExpressionVisitor : System.Linq.Expressions.ExpressionVisitor {
        public Dictionary<Expression, Expression> subst = new Dictionary<Expression, Expression>();

        protected override Expression VisitParameter(ParameterExpression node) {
            Expression newValue;
            if (subst.TryGetValue(node, out newValue)) {
                return newValue;
            }
            return node;
        }
    }


查看完整回答
反对 回复 2019-06-19
  • 3 回答
  • 0 关注
  • 1685 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信