考虑具有以下列和相应实体的单个表(称为 Car)的情况
string Make
string Model
string Owner
现在我想创建一个搜索,用户可以在其中选择(通过使用复选框)搜索应该定位到哪些属性。如果选择了多个,那么如果在其中至少一个中找到搜索字符串就足够了。
此外,如果给出了多个搜索字符串(由空格分隔),则搜索应该仅在找到每个单词时匹配(例如,给定搜索字符串“ter mist”,拥有“先生”的汽车将匹配)。
在做了一些研究之后,我想我会Expression<Func<Car, bool>>
为每个选择的属性创建一个列表,为搜索字符串中的每个单词添加一个,然后将所有这些一起创建一个Expression<Func<Car, bool>>
. 一旦我为所有选择的特性获得了这些,我会将它们组合在一起,以创建最终过滤器。然而,这正是我苦苦挣扎的地方。
最后,我得到的最远的是 NotSupportedException 说The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
这是我用于组合的辅助函数(来自http://social.msdn.microsoft.com/Forums/en-US/linqprojectgeneral/thread/60a1f4c0-d4d9-4143-91aa-79d29dde7a7c/):
public static Expression<Func<T, bool>> Or<T>(params Expression<Func<T, bool>>[] predicates)
{
if (predicates.Length == 1)
return predicates[0];
Expression<Func<T, bool>> result = predicates[0];
for (int i = 1; i < predicates.Length; i++)
{
result = OrTwo(result, predicates[i]);
}
return result;
}
private static Expression<Func<T, bool>> OrTwo<T>(Expression<Func<T, Boolean>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return (Expression.Lambda<Func<T, Boolean>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters));
}
这一切也变得令人惊讶地令人困惑,所以我开始认为必须有一种更简单的方法来解决这个问题。那么,解决这个问题的最简单方法是什么?
解决方案
在尝试了几件事(LINQKit、Albahari 的 PredicateBuilder、自己摆弄表达式树)之后,我终于来到了这里。PredicateBuilder 的这个通用版本在没有任何其他外部依赖项的情况下工作,并且与 EF 完全兼容。它使解决问题变得非常简单。