【问题标题】:Replacing the parameter name in the Body of an Expression替换表达式正文中的参数名称
【发布时间】:2011-03-25 10:06:33
【问题描述】:

我正在尝试基于规范对象动态构建表达式。

我创建了一个 ExpressionHelper 类,它有一个像这样的私有表达式:

private Expression<Func<T, bool>> expression;

public ExpressionHelper()
{
    expression = (Expression<Func<T, bool>>)(a => true);
}

然后一些简单的方法如下:

public void And(Expression<Func<T,bool>> exp);

我正在与 And 方法的主体作斗争。我基本上是想把exp的正文撕下来,用expression中的参数替换所有参数,然后将其附加到expression正文的末尾和AndAlso。

我已经这样做了:

var newBody = Expression.And(expression.Body,exp.Body);

expression = expression.Update(newBody, expression.Parameters);

但最终我的表情看起来像这样:

{ a => e.IsActive && e.IsManaged }

有没有更简单的方法来做到这一点?或者我怎样才能撕掉那些 e 并用 a 替换它们?

【问题讨论】:

    标签: c# linq expression-trees


    【解决方案1】:

    这里最简单的做法是Expression.Invoke,例如:

    public static Expression<Func<T, bool>> AndAlso<T>(
        Expression<Func<T, bool>> x, Expression<Func<T, bool>> y)
    {
        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(x.Body, Expression.Invoke(y, x.Parameters)),
            x.Parameters);
    }
    

    这适用于 LINQ-to-Objects 和 LINQ-to-SQL,但不受 EF 支持。遗憾的是,对于 EF,您需要使用访问者来重写树。

    使用来自:Combining two lambda expressions in c#的代码

    public static Expression<Func<T, bool>> AndAlso<T>(
        Expression<Func<T, bool>> x, Expression<Func<T, bool>> y)
    {
        var newY = new ExpressionRewriter().Subst(y.Parameters[0], x.Parameters[0]).Inline().Apply(y.Body);
    
        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(x.Body, newY),
            x.Parameters);
    }
    

    或者在 .NET 4.0 中,使用ExpressionVisitor

    class ParameterVisitor : ExpressionVisitor
    {
        private readonly ReadOnlyCollection<ParameterExpression> from, to;
        public ParameterVisitor(
            ReadOnlyCollection<ParameterExpression> from,
            ReadOnlyCollection<ParameterExpression> to)
        {
            if(from == null) throw new ArgumentNullException("from");
            if(to == null) throw new ArgumentNullException("to");
            if(from.Count != to.Count) throw new InvalidOperationException(
                 "Parameter lengths must match");
            this.from = from;
            this.to = to;
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            for (int i = 0; i < from.Count; i++)
            {
                if (node == from[i]) return to[i];
            }
            return node;
        }
    }
    public static Expression<Func<T, bool>> AndAlso<T>(
          Expression<Func<T, bool>> x, Expression<Func<T, bool>> y)
    {
        var newY = new ParameterVisitor(y.Parameters, x.Parameters)
                  .VisitAndConvert(y.Body, "AndAlso");
        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(x.Body, newY),
            x.Parameters);
    }
    

    【讨论】:

    • 该死,只是让 Invoke 在内存中工作,并在发生这种情况时在 EF 上进行测试。谢谢。
    • 4.0 还具有一个内置的 ExpressionVisitor,可能值得一看 - 如果您使用的是 4.0!
    • @robert - 添加了一个不需要这个的版本
    • 我建议断言to.Count == from.Count
    • @MarcGravell 非常感谢您发布此答案,非常有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-07
    • 1970-01-01
    • 2019-04-15
    • 1970-01-01
    相关资源
    最近更新 更多