3

我正在尝试构建一个通用查询构建器来接受用户输入的搜索词。为了获得我需要的 where 子句,我必须解析术语并独立确定需要包含在子句中的字段。

请注意,此示例已大大简化以说明这一点。我知道在这种特殊情况下,整个结果可以表示为单个 Where() 语句。这段代码在我的问题空间中工作(单个 Where() 语句不起作用),所以答案必须解决这里实际发生的事情,而不是如何使它更简单。

我从一个术语列表开始(这里表示为一个字符串 [],但最终将是一个更复杂类型的 IList,有助于指导查询构建器):

    string[] terms = new string[] {
        "hope",
        "bob",
    };

第一个示例(如下)给出了正确的结果集(在三个搜索字段中的任何一个中具有“bob”的员工并且在三个匹配字段中的任何一个中都具有“hope”的任何记录)。这确实表明在链接 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 上,而另一些将在“站点”的其他字段上,所以在编译时我必须能够迭代并独特地对待每个术语。)

下一个示例(如下)是我想要做的,但它不尊重第一个术语,并且只匹配最后一个;无论“hope”是否出现在任何字段中,任何字段中包含“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() 指令时给出了“索引越界”错误:

    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 的值)。这也与我在 query1 中看到的结果一致。

有谁知道绑定是如何工作的以及我如何构建我的查询?

4

1 回答 1

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));
}
于 2012-12-07T17:54:10.697 回答