使用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"参数在哪里丢失。我不知道还缺什么能让这一切顺利进行。我的代码与许多其他完美工作的示例相同。欢迎提供有关调用表达式及其在幕后如何工作的任何附加理论信息。

使用foreach循环构建一个自定义谓词作为过滤器

我不明白你为什么这样做:

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);
    }
}