3

ユーザーが入力した検索用語を受け入れるための汎用クエリビルダーを構築しようとしています。必要なwhere句を取得するには、用語を解析し、句に含める必要のあるフィールドを個別に決定する必要があります。

この例は、要点を説明するために大幅に簡略化されていることに注意してください。この特定のケースでは、結果全体を単一のWhere()ステートメントとして表現できることを私は知っています。このコード私の問題空間(単一のWhere()ステートメントでは機能しない)で機能するため、答えは、単純にする方法ではなく、ここで実際に起こっていることに対処する必要があります。

用語のリストから始めます(ここではstring []として表されますが、最終的にはクエリビルダーのガイドに役立つより複雑なタイプのIListになります)。

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

この最初の例(以下)は、正しい結果セットを示しています(3つの検索フィールドのいずれかに「bob」があり、3つのフィールドのいずれかに「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

最後の例は、値ではなく変数をキャプチャするデモンストレーションです。クエリを実行するまでに、値が になるため、問題が発生します。最小の変更は、次を使用することです。iterms.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ループ全体で 1 つの変数があり、代わりにこれを使用する必要があるため、これは機能しません。

// 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 に答える