【问题标题】:Dynamic where clause to filter collection elements用于过滤集合元素的动态 where 子句
【发布时间】:2015-05-14 05:48:43
【问题描述】:

我有一个集合List<List<object>>,我需要根据List<object> 集合包含给定元素的情况过滤掉它。我能够构建 where 子句,但出现以下异常:

System.Core.dll 中出现“System.InvalidOperationException”类型的异常,但未在用户代码中处理 附加信息:从范围“”引用的“System.Collections.Generic.List`1[System.Object]”类型的变量“x”,但未定义

我发现了类似的问题,并且我知道问题出在哪里,但我需要帮助来寻找解决方案。

这是我的代码:

        protected override Expression<Func<List<object>, bool>> GetWhereClause()
        {
            var type = typeof(List<object>);
            var parameterExpression = Expression.Parameter(type, "x");                

            Expression expressionBody = null;
            if (Verified)
            {
                Expression<Func<List<object>, bool>> expression = x => x.Contains("Verified");
                expressionBody = expression.Body;
            }
            if (GoodMatch)
            {
                Expression<Func<List<object>, bool>> expression = x => x.Contains("Good Match");
                if (expressionBody != null)
                    expressionBody = Expression.Or(expressionBody, expression.Body);
                else
                    expressionBody = expression.Body;
            }
            //More conditions here
            if (expressionBody != null)
            {
                var whereClauseExp = Expression.Lambda<Func<List<object>, bool>>(expressionBody, parameterExpression);

                return whereClauseExp;
            }

            return null;
        } 

现在,此方法生成所需的 where 子句,但是当我尝试应用它时,我得到了提到的异常。

        if (whereClause != null)
        {
            items = items.Where(whereClause.Compile());
            //
        }

【问题讨论】:

  • 你也可以提供堆栈跟踪吗?

标签: c# linq expression-trees


【解决方案1】:

我有一个类似的用例需要动态 where 子句并使用 Predicate Builder

使用它,您可以执行以下操作:*

private Expression<Func<List<T>, bool>> GetWhereClause<T>(T itemToFind){
        var predicate = PredicateBuilder.False<List<T>>();

        if(Verified) {
            predicate = predicate.And(p => p.Contains(itemToFind));
        }

        if(GoodMatch) {
            predicate = predicate.Or(p => p.Contains(itemToFind));
        }

        return predicate;
}

【讨论】:

  • 谢谢,我明天去看看。
【解决方案2】:

您不能在新表达式中“按原样”使用来自另一个表达式的参数。当你这样做时:

Expression<Func<List<object>, bool>> expression = x => x.Contains("Verified");
expressionBody = expression.Body;

那么您只需在正文中包含内联定义的参数 x 。现在这个参数不是你之前定义的那个参数了

var parameterExpression = Expression.Parameter(type, "x"); 

即使他们都有名字 x,这还不够。表达式树具有引用相等性。 所以要让它工作,只需使用一个访问者,它将用你的参数替换参数。创建访问者:

public class ParameterUpdateVisitor : ExpressionVisitor
{
    private ParameterExpression _parameter;

    public ParameterUpdateVisitor(ParameterExpression parameter)
    {
        _parameter = parameter;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return _parameter;
    }
}

然后在你的代码中使用它:

Expression<Func<List<object>, bool>> expression = x => x.Contains("Verified");
var visitor = new ParameterUpdateVisitor(parameterExpression);
expressionBody = visitor.Visit(expression.Body);

当然,这适用于来自另一个表达式树的每个部分。

注意!!!此访问者更加简化,仅用于您的示例。如果您的表达式可能具有具有自己参数的方法,请确保仅替换您想要的参数! 例如。它不适用于:

Expression<Func<List<object>, bool>> expression = x => x.Select(o => o.ToString()).Contains("Verified");

因为此访问者也会替换“o”。如果你有这种情况,那么在构造函数中也传入你要替换的参数(例如x,即expression.Parameters.First()),并且仅在node == myOldParameter的情况下在被覆盖的方法中替换。

顺便说一句:如果你在最后编译东西,为什么还需要表达式树?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-25
    • 2012-06-20
    • 1970-01-01
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多