0

我正在尝试使用 LINQ to SQL 创建一个简单的可重用搜索。

我传入了在搜索框中输入的单词列表。然后根据此标准过滤结果。

  private IQueryable<User> BasicNameSearch(IQueryable<User> usersToSearch, ICollection<string> individualWordsFromSearch)
  {
        return usersToSearch
            .Where(user => 
            individualWordsFromSearch.Contains(user.Forename.ToLower()) 
            || individualWordsFromSearch.Contains(user.Surname.ToLower()));
  }

现在我希望在不同的数据源上使用相同的搜索功能,并希望动态选择要应用搜索的字段。例如,代替 IQueryable 的用户,我可能有一个 IQueryable 的汽车,而不是名字和姓氏,搜索离开了品牌和型号。基本上,目标是通过在运行时动态选择要搜索的内容来重用搜索逻辑。

4

2 回答 2

2

您可以创建一个扩展方法,将您的string选择器一起编译成一个表达式:

public static class CompileExpressions
{
    public static IQueryable<T> SearchTextFieldsOr<T>(this IQueryable<T> source,
        ICollection<string> wordsFromSearch, params Func<T, string>[] stringSelectors)
    {
        Expression<Func<T, bool>> compiledExpression = t => false;

        foreach (var filter in stringSelectors)
        {
            compiledExpression = compiledExpression.Or(t => wordsFromSearch.Contains(filter(t)));
        }

        var compiled = compiledExpression.Compile();

        return source.Where(t => compiled(t));
    }

    public static IQueryable<T> SearchTextFieldsAnd<T>(this IQueryable<T> source,
        ICollection<string> wordsFromSearch, params Func<T, string>[] stringSelectors)
    {
        foreach (var filter in stringSelectors)
        {
            source = source.Where(t => wordsFromSearch.Contains(filter(t)));
        }

        return source;
    }

    //Taken from http://www.albahari.com/nutshell/predicatebuilder.aspx
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                                Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
    }
}

如何使用它的一个例子:

public class Entity
{
    public string Name { get; set; }
    public string Type { get; set; }
    public string Model { get; set; }
    public string Colour { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var source = new[]{
            new Entity { Colour = "Red", Model = "New", Name="Super", Type="Filter"},
            new Entity { Colour = "Green", Model = "New", Name="Super", Type="Filter"},
            new Entity { Colour = "Green", Model = "New", Name="Super", Type="Filter"},
            new Entity { Colour = "Green", Model = "New", Name="Super", Type="Filter"},
            new Entity { Colour = "Green", Model = "New", Name="Super", Type="Filter"},
            new Entity { Colour = "Green", Model = "New", Name="Super", Type="Amazing"},
        };

        var filters = new[] {"Red", "Amazing" };

        var filteredOr = source
               .AsQueryable()
               .SearchTextFieldsOr(filters, t => t.Colour, t => t.Type)
               .ToList();

        //2 records found because we're filtering on "Colour" OR "Type"

        var filteredAnd = source
               .AsQueryable()
               .SearchTextFieldsAnd(filters, t => t.Colour, t => t.Type)
               .ToList();

         //1 record found because we're filtering on "Colour" AND "Type"

    }
}

因为您的string选择器参数是 type params Func<T, string>[],所以您可以添加任意数量的string选择器以包含在查询中。

于 2013-11-04T16:09:30.983 回答
0

你的问题类似于这个线程的(虽然不是同一个问题)。

简而言之,当您编写一个 linq-to-sql 请求时,它只构建一个与您键入的实际代码相对应的 System.Linq.Expression,该代码被提供给“提供者”,它会将其转换为 sql(您可以获得此提供程序通过将您的请求转换为具有 Provider 属性的 IQueryable)。

事实上,形成您请求的代码永远不会被“执行”,实际上甚至不会像您传递给 Linq-to-objects 函数的委托那样编译为 IL。(使用System.Linq.Enumerable扩展方法,而 linq-to-sql使用扩展方法System.Linq.Queryable

因此,您还可以“手动”创建 linq 表达式(而不是让 c# 编译器在后台为您构建它),并将它们传递给提供者,提供者将解析和执行它们,就像您使用常规方式创建它们一样。

请参阅上面给出的线程中的示例。

编辑:或者你可以看看奥利弗的答案,他给了你一个复制粘贴运行示例:)

于 2013-11-04T16:09:21.337 回答