最简单的方法创建一个动态搜索应用于IQueryable
本文关键字:动态 一个 搜索 应用于 IQueryable 方法 创建 最简单 | 更新日期: 2023-09-27 18:04:35
假设有一个表(称为Car),其中包含以下列和相应的实体
string Make
string Model
string Owner
现在我想创建一个搜索,用户可以选择(通过使用复选框)搜索应该针对哪些属性。如果选择了多个,那么只要在其中至少一个中找到搜索字符串就足够了。
此外,如果给出了多个搜索字符串(以空格分隔),则搜索应该只在找到每个单词时才匹配(例如,给定搜索字符串"ter mist",则匹配车主为"mister"的汽车)。
在做了一些研究之后,我想我应该为每个选择的属性创建一个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完全兼容。
标题>几个星期前我决定自己解决这个问题。
查看我的博客:
http://jnye.co/Posts/5/generic-repository-search-function-with-expression-trees链接更新:我现在已经创建了一个新的帖子,将搜索功能作为扩展方法添加到IQueryable:
http://jnye.co/Posts/6/c%23-generic-search-extension-method-for-iqueryable你应该能够使它适应你的需要。
下面是我创建的Search函数的摘录。
/// <summary>
/// Performs a search on the supplied string property
/// </summary>
/// <param name="stringProperty">Property to search upon</param>
/// <param name="searchTerm">Search term</param>
public virtual IQueryable<T> Search(Expression<Func<T, string>> stringProperty, string searchTerm)
{
var source = this.RetrieveAll();
if (String.IsNullOrEmpty(searchTerm))
{
return source;
}
//Create expression to represent T.[property] != null
var isNotNullExpression = Expression.NotEqual(stringProperty.Body, Expression.Constant(null));
//Create expression to represent T.[property].Contains(searchTerm)
var searchTermExpression = Expression.Constant(searchTerm);
var checkContainsExpression = Expression.Call(stringProperty.Body, typeof(string).GetMethod("Contains"), searchTermExpression);
//Join not null and contains expressions
var notNullAndContainsExpression = Expression.AndAlso(isNotNullExpression, checkContainsExpression);
//Build final expression
var methodCallExpression = Expression.Call(typeof (Queryable),
"Where",
new Type[] {source.ElementType},
source.Expression,
Expression.Lambda<Func<Club, bool>>(notNullAndContainsExpression, stringProperty.Parameters));
return source.Provider.CreateQuery<T>(methodCallExpression);
}
您应该能够重构生成methodCallExpression
的代码,以创建多个搜索表达式,然后可以使用Expression.OrElse()
将其组合起来。