以下是一些示例数据:
List<Book> books = new List<Book>()
{
new Book(){Title = "artemis fowl: the time paradox", Pages = 380},
new Book(){Title = "the lieutenant", Pages = 258},
new Book(){Title = "the wheel of time", Pages = 1032},
new Book(){Title = "ender's game", Pages = 404},
new Book(){Title = "the sphere", Pages = 657}
};
背景
以上使用Book类的简化版本。当然,它包含许多领域。我的最终目标是允许用户执行“高级”搜索,允许用户指定任何字段,并进一步允许用户使用布尔代数为特定字段指定关键字。
例如:在标题搜索文本框中:+(cake | pastry)+ ~demon
上述意思是:找到标题中有“蛋糕”或“糕点”字样的所有书籍,并且没有“恶魔”这个词。
问题:
宝贝步骤将导致最终的解决方案。所以我最初有以下代码:
List<Func<Book, bool>> fs = new List<Func<Book, bool>>()
{
b => b.Title.Contains("me"),
b => b.Title.Contains("the")
};
var q2 = from b in books select b;
foreach (var f in fs)
q2 = q2.Where(f);
foreach (Book b in q2)
{
Console.WriteLine("Title:\t\t{0}\nPages:\t\t{1}\n",
b.Title, b.Pages);
}
上面的代码工作正常。它会在标题中查找包含“和'我'的书籍。
第2阶段
现在上面的过滤器类型为Func&lt; Book ,bool&gt;。该类将是一个实体框架生成的类,我不想在我的UI层中使用,其中将输入搜索短语,并将生成搜索过滤器以传递给BLL。
所以我有以下三次尝试:
var q = from b in books select b;
List<Func<string, bool>> filters = new List<Func<string, bool>>()
{
s => s.Contains("me"),
s => s.Contains("the"),
};
//This works...
for (int i = 0; i != filters.Count; ++i)
{
Func<string, bool> daF = filters[i];
q = q.Where(b => (daF(b.Title)));
}
//This produces an exception...
//Due to index in query?
// for (int i = 0; i != filters.Count; ++i)
// {
// q = q.Where(b => ((filters[i])(b.Title)));
// }
//This runs but doesn't produce the proper output
// foreach (Func<string, bool> filter in filters)
// q = q.Where(b => filter(b.Title));
foreach (Book b in q)
{
Console.WriteLine("Title:\t\t{0}\nPages:\t\t{1}\n",
b.Title, b.Pages);
}
第一个注释掉的部分触发索引器超出范围异常,表明i的值为2。
第二个评论出来的作品运行并产生输出,但它打印出5本书中的四本......除了标题为“恩德的游戏”的书外。那不对......
所以,阅读我的帖子,我发现我无法记住解释每一个细节的坏习惯......
所以你去吧。请解释为什么不同的输出。我想你可能暗示我目前的“解决方案”可能有所改进。
答案 0 :(得分:4)
由于我们在这里使用LINQ to Objects,您应该可以使用All()
。那你就不需要循环了。
var query = books.Where(book => filters.All(filter => filter(book.Title)));
相当于:
var query = from book in books
where filters.All(filter => filter(book.Title))
select book;
至于其他尝试不起作用的原因,你是closing over the loop variable。通常,在循环中使用lambda函数时应该小心。简单的解决方法是声明一个在lambdas中使用的单独变量。请注意,您实际上是在第一个查询中间接执行此操作。但是你根本不需要循环,应该使用上面的一个查询。
for (int i = 0; i != filters.Count; ++i)
{
var index = i;
q = q.Where(b => filters[index](b.Title));
}
foreach (Func<string, bool> f in filters)
{
var filter = f;
q = q.Where(b => filter(b.Title));
}