我正在尝试构建一个通用查询构建器,用于接受用户输入的搜索词。为了得到我需要的where子句,我必须解析术语并独立地确定哪个字段需要包含在该子句中。
请注意,此示例大大简化以说明这一点。我知道在这种特殊情况下,整个结果可以表示为单个Where()语句。这段代码将在我的问题空间中工作(其中一个Where()语句不会),所以答案必须解决实际发生的问题,而不是如何简化它。
我从一个术语列表开始(这里表示为一个字符串[],但最终将是一个更复杂类型的IList,它有助于指导查询构建器):
string[] terms = new string[] {
"hope",
"bob",
};
这个第一个例子(下面)给出了正确的结果集(任何一个员工在三个搜索字段中都有'bob'的记录,并且在三个字段匹配中都有'希望')。这确实证明了在链接Where()子句时使用代码构建了正确的查询:
var query0 = Sites.Where(s => s.SiteId < 200);
query0 = query0.Where(s =>
s.Employee.FirstName.Contains(terms[0]) ||
s.Employee.LastName.Contains(terms[0]) ||
s.Employee.Username.Contains(terms[0]));
query0 = query0.Where(s =>
s.Employee.FirstName.Contains(terms[1]) ||
s.Employee.LastName.Contains(terms[1]) ||
s.Employee.Username.Contains(terms[1]));
query0.Dump();
(请注意,我不能使用这种直接方法,因为我不知道会有多少条款,其中一些将在Employee上,而其他一些将在'Sites'的其他领域,所以在编译时时间我必须能够独特地迭代和处理每个术语。)
下一个示例(下面)是我想要做的事情,但它不尊重第一个术语,只匹配最后一个术语;无论“希望”是否出现在任何字段中,都包含任何字段中包含“bob”的记录:
var query1 = Sites.Where(s => s.SiteId < 200);
foreach (string term in terms)
{
query1 = query1.Where(s =>
s.Employee.FirstName.Contains(term) ||
s.Employee.LastName.Contains(term) ||
s.Employee.Username.Contains(term));
}
query1.Dump();
这个最后的例子(下面)在到达Dump()指令时给出了'index out of bounds'错误:
var query2 = Sites.Where(s => s.SiteId < 200);
for (int i = 0; i < terms.Length; ++i)
{
query2 = query2.Where(s =>
s.Employee.FirstName.Contains(terms[i]) ||
s.Employee.LastName.Contains(terms[i]) ||
s.Employee.Username.Contains(terms[i]));
}
query2.Dump();
我认为query2是最有说服力的例子。这几乎就像LINQ在构建完整个查询后尝试将变量绑定到SQL参数一样,并且它试图使用i == 2(这将是循环退出时i的值) all 绑定。这也与我在query1中看到的结果一致。
有谁知道绑定是如何工作的以及如何构建我的查询?
答案 0 :(得分:2)
最后一个示例是演示您捕获变量,而不是值。当您执行查询时,i
的值为terms.Length
,因此您遇到了问题。最小的变化是使用:
for (int i = 0; i < terms.Length; ++i)
{
int copy = i;
query2 = query2.Where(s =>
s.Employee.FirstName.Contains(terms[copy]) ||
s.Employee.LastName.Contains(terms[copy]) ||
s.Employee.Username.Contains(terms[copy]));
}
现在循环的每次迭代都有一个名为copy
的单独变量,它不会改变 - 所以你很好。
现在,使用foreach
循环会更清晰,但现在取决于您是否使用C#5。在C#5中,您可以使用:
// Only works in C# 5
foreach (string term in terms)
{
query2 = query2.Where(s =>
s.Employee.FirstName.Contains(term) ||
s.Employee.LastName.Contains(term) ||
s.Employee.Username.Contains(term));
}
但是在C#3或C#4中,这是行不通的,因为你在整个循环中都有一个term
变量,而你必须使用它:
// Works in C# 3+
foreach (string term in terms)
{
string copy = term;
query2 = query2.Where(s =>
s.Employee.FirstName.Contains(copy) ||
s.Employee.LastName.Contains(copy) ||
s.Employee.Username.Contains(copy));
}