过滤集合元素的动态where子句
Dynamic where clause to filter collection elements
我有一个集合 List<List<object>>
,我需要根据包含给定元素的 List<object>
集合过滤掉它。我能够构建 where 子句,但出现以下异常:
An exception of type 'System.InvalidOperationException' occurred in System.Core.dll but was not handled in user code
Additional information: variable 'x' of type 'System.Collections.Generic.List`1[System.Object]' referenced from scope '', but it is not defined
我发现了类似的问题,我知道问题出在哪里,但我需要帮助才能找到解决方案。
这是我的代码:
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());
//
}
我有一个类似的用例需要动态 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;
}
您不能在新表达式中使用来自另一个表达式 'as is' 的参数。当你这样做时:
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.[=15 时才在重写的方法中替换=]
顺便说一句:如果无论如何都在最后编译,为什么还需要表达式树?
我有一个集合 List<List<object>>
,我需要根据包含给定元素的 List<object>
集合过滤掉它。我能够构建 where 子句,但出现以下异常:
An exception of type 'System.InvalidOperationException' occurred in System.Core.dll but was not handled in user code Additional information: variable 'x' of type 'System.Collections.Generic.List`1[System.Object]' referenced from scope '', but it is not defined
我发现了类似的问题,我知道问题出在哪里,但我需要帮助才能找到解决方案。
这是我的代码:
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());
//
}
我有一个类似的用例需要动态 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;
}
您不能在新表达式中使用来自另一个表达式 'as is' 的参数。当你这样做时:
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.[=15 时才在重写的方法中替换=]
顺便说一句:如果无论如何都在最后编译,为什么还需要表达式树?