【问题标题】:Simplest way to create a dynamic search to be applied on IQueryable创建要应用于 IQueryable 的动态搜索的最简单方法
【发布时间】:2013-03-08 05:24:38
【问题描述】:

考虑有一个包含以下列和相应实体的表(称为 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's PredicateBuilder、自己摆弄表达式树)之后,我终于找到了here。 PredicateBuilder 的这个通用版本在没有任何其他外部依赖项的情况下工作,并且与 EF 完全兼容。它使解决问题变得非常简单。

【问题讨论】:

  • 您考虑使用Dynamic LINQ 库吗?
  • 哦,没听说过,谢谢,我去看看。

标签: c# linq entity-framework lambda linq-to-entities


【解决方案1】:

几周前我决定自己解决这个问题。

查看我的博文:

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

您应该能够根据自己的需要进行调整。

更新

以下是我创建的搜索功能的摘录。

/// <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() 组合这些表达式。

【讨论】:

  • 链接失效后,这个答案就没用了。请在此处解释博客文章或删除您的答案并添加评论。简而言之:SO 上不允许仅链接答案。
  • 该死,我真的希望有一种更简单、更简洁的方法来实现这一点,但我想我在那里不太走运。谢谢,那我试试这个方法!
  • 今晚我会对此有所了解,因为我自己也会感兴趣。
猜你喜欢
  • 2010-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多