我正在尝试使用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 of Cars而不是名字和姓氏,搜索从Make和Model开始。基本上,目标是通过动态选择在运行时搜索的内容来重用搜索逻辑。
答案 0 :(得分: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
选择参数的类型为params Func<T, string>[]
,因此您可以添加任意数量的string
选择器,以便包含在查询中。
答案 1 :(得分:0)
您的问题与this thread's类似(但不是同一个问题)。
简而言之,当您编写linq-to-sql请求时,它只构建一个System.Linq.Expression,对应于您输入的实际代码,该代码将提供给“provider”,后者将其转换为sql(您可以通过将您的请求转发给具有Provider属性的IQueryable来获得此提供程序。
实际上,形成你请求的代码永远不会被“执行”,实际上甚至不会像你传递给Linq-to-objects函数的委托那样编译成IL。 (使用System.Linq.Enumerable
扩展方法,而linq-to-sql使用System.Linq.Queryable
个扩展方法
因此,您也可以“手动”创建linq表达式(而不是让c#编译器为您构建它),并将它们传递给提供程序,提供程序将解析并执行它们,就像您使用常规创建它们一样方式。
参见上面给出的主题中的例子。
编辑或者您可以查看Oliver的回答,他给了您一个复制粘贴运行的示例:)