【问题标题】:Aggregate Linq Expressions: getting an exception of variable referenced from scope聚合 Linq 表达式:获取从范围引用的变量的异常
【发布时间】:2018-02-12 23:48:09
【问题描述】:

我有一个类型的表达式列表:Expression<Func<Person, bool>>,我想聚合它们,然后将聚合结果编译成单个 Func<Person, bool>。我能够创建聚合表达式,但编译结果聚合表达式的部分会引发异常。任何帮助,将不胜感激。谢谢。

Expression<Func<Person, bool>> expr1 = x => x.Age > 10;
Expression<Func<Person, bool>> expr2 = x => x.LastName == "some firstname";
Expression<Func<Person, bool>> expr3 = x => x.FirstName == "some lastname";
Expression<Func<Person, bool>> expr4 = x => x.Initial == 'a';
Expression<Func<Person, bool>> expr5 = x => x.DateOfBirth == DateTime.Now;
Expression<Func<Person, bool>> expr6 = x => x.Height > 10;

var exprList = new List<Expression<Func<Person, bool>>>()
{
    expr1, expr2, expr3, expr4, expr5
};


var list = exprList
        .Select(x => x.Body)
        .Aggregate(Expression.AndAlso);

// this works, apparently?!
var aggregatedExpression = Expression.Lambda<Func<Person, bool>>(list, Expression.Parameter(typeof(Person), "x"));

// fails here! it cannot compile
var result = aggregatedExpression.Compile();

这是个例外:

未处理的异常:System.InvalidOperationException:从范围“”引用的“TestAggregateExpression.Person”类型的变量“x”,但未定义

在 System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression 节点,VariableStorageKind 存储)

【问题讨论】:

    标签: c# linq expression-trees


    【解决方案1】:

    我相信您需要访问列表中的所有表达式并替换参数。使用这个助手:

    internal sealed class ParameterReplacer : ExpressionVisitor
    {
        private readonly ParameterExpression _param;
    
        private ParameterReplacer(ParameterExpression param)
        {
            _param = param;
        }
    
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node.Type == _param.Type ?
                base.VisitParameter(_param) : // replace
                node; // ignore
        }
    
        public static T Replace<T>(ParameterExpression param, T exp) where T : Expression
        {
            return (T)new ParameterReplacer(param).Visit(exp);
        }
    }
    

    用法:

    var param = Expression.Parameter(typeof(Person), "x"); // I'd use 'p' by the way
    exp = ParameterReplacer.Replace(param, exp);
    

    在你的情况下:

    var list = exprList.Select(x => x.Body)
                       .Select(exp => ParameterReplacer.Replace(param, exp))
                       .Aggregate(Expression.AndAlso);
    

    【讨论】:

    • 我用一个使用示例更新了我的答案。请尝试让我知道。如果它有效,那么您将面临同样的问题,我之前也遇到过。我会尽力解释。
    • @Node.JS:如果我没记错的话,这个异常是由于尽管参数看起来相同但这个参数的上下文不同而引起的。因此,您需要在重用它的所有表达式中统一它。
    • 将相同的param 传递给Expression.Lambda
    猜你喜欢
    • 1970-01-01
    • 2015-03-09
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-07
    相关资源
    最近更新 更多