使用foreach循环构建一个自定义谓词作为过滤器
本文关键字:自定义 一个 谓词 过滤器 循环 foreach 构建 使用 | 更新日期: 2023-09-27 18:13:06
我需要通过将它们传递给自定义过滤器来过滤文档列表,我正在努力使用foreach
循环动态构建:
var mainPredicate = PredicateBuilder.True<Document>();
// mainPredicate is combined to other filters successfully here ...
var innerPredicate = PredicateBuilder.False<Document>();
foreach (var period in periods)
{
var p = period;
Expression<Func<Document, bool>> inPeriod =
d => d.Date >= p.DateFrom && d.Date <= p.DateTo;
innerPredicate = innerPredicate.Or(d => inPeriod.Invoke(d));
}
mainPredicate = mainPredicate.And(innerPredicate);
最后一行:
documents = this.ObjectSet.AsExpandable().Where(mainPredicate).ToList();
抛出这个异常:
参数d没有在指定的LINQ中绑定到实体。处理步骤查询表达式。
有人知道为什么我得到这个异常吗?我不明白我传递给InPeriod方法的"d"参数在哪里丢失。我不知道还缺什么能让这一切顺利进行。我的代码与许多其他完美工作的示例相同。欢迎提供有关调用表达式及其在幕后如何工作的任何附加理论信息。
我不明白你为什么这样做:
innerPredicate = innerPredicate.Or(d => inPeriod.Invoke(d));
当你可以完全避免Invoke
时,像这样:
innerPredicate = innerPredicate.Or(inPeriod);
这应该可以很好地工作。
顺便说一句,我感觉这里有一个bug与LINQKit(除非有一些文档表明它不支持这种情况)。
当我尝试这个类似的代码:
Expression<Func<int, bool>> first = p1 => p1 > 4;
Expression<Func<int, bool>> second = p2 => p2 < 2;
// Expand is similar to AsExpandable, except it works on
// expressions, not queryables.
var composite = first.Or(d => second.Invoke(d))
.Expand();
…LINQKit生成了以下复合表达式:
p1 => ((p1 > 4) OrElse (d < 2)) // what on earth is d?
… (NodeType = parameter, Name = 'd')。
用first.Or(second).Expand()
回避Invoke
产生完全合理的:
p1 => ((p1 > 4) OrElse (p1 < 2)) // much better now...
最后,我找到了一种避免将多个谓词组合到主表达式树的方法。
假设每个谓词代表一个不同的过滤器,并且我希望最终的组合过滤器是一系列必须遵守的条件,我们可以说每个谓词都必须返回true以使最终谓词返回true。
要使工作,谓词必须与 AND
组合。因此,结果SQL查询必须如下所示:
predicate1 AND predicate2 AND predicate3
…
将这些谓词与AND
结合起来的更好方法是将Where
查询操作符链接到最终查询,如下所示:
var documents = this.ObjectSet.AsExpandable()
.Where(mainPredicate)
.Where(otherPredicate)
.Where(yetAnotherPredicate)
.ToList();
生成的SQL查询将这些谓词与AND
组合在一起。那正是我想做的。
我使用这些扩展方法:
public static class Extensions
{
public static Expression<Func<T, bool>> OrElse<T>(this Expression<Func<T, bool>> source, Expression<Func<T, bool>> predicate)
{
InvocationExpression invokedExpression = Expression.Invoke(predicate, source.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(source.Body, invokedExpression), source.Parameters);
}
public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> source, Expression<Func<T, bool>> predicate)
{
InvocationExpression invokedExpression = Expression.Invoke(predicate, source.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(source.Body, invokedExpression), source.Parameters);
}
}