2

データベーステーブルでキーワード検索を実行するクエリを作成する必要があります。現在、コードは次のようになっています(ただし、ハードコードされたキーワードのセットがあります)。

var keywords = new [] { "alpha", "bravo", "charlie" };
IQueryable<Story> stories = DataContext.Stories;

foreach( var keyword in keywords )
{
    stories = from story in stories where story.Name.Contains ( keyword ) );
}

return stories;

ReSharperは、foreach内のキーワードに対して「変更されたクロージャへのアクセス」警告をスローします。エラーを理解し、生成されたSQLを確認して問題を確認します。

SELECT [t0].[Id], [t0].[Name]
FROM [dbo].[Story] AS [t0]
WHERE (([t0].[Name] LIKE @p0))
  AND (([t0].[Name] LIKE @p1))
  AND (([t0].[Name] LIKE @p2))
-- @p0: Input NVarChar (Size = 9; Prec = 0; Scale = 0) [%charlie%]
-- @p1: Input NVarChar (Size = 9; Prec = 0; Scale = 0) [%charlie%]
-- @p2: Input NVarChar (Size = 9; Prec = 0; Scale = 0) [%charlie%]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1

キーワードイテレータはループ中に変更されるため、私のSQLには最後の値( "charlie")への参照のみが含まれています。

この問題を回避するにはどうすればよいですか?新しいキーワードwhere句を適用する前に、クエリ可能なストーリーをリストに変換することもできますが、それは非効率的です。

解決済み

すべての答えをありがとう。最終的に、2つの別々の問題が発生しましたが、どちらも解決されました。

  1. 「変更されたクロージャへのアクセス」の問題を回避するには、foreach()ループ内でローカル変数を使用します。
  2. LINQKitのPredicateBuilderを使用して、OR句のリストを動的にアセンブルし、「任意の」スタイルのキーワード検索を可能にします。
4

6 に答える 6

5

foreachブロックのスコープ内の一時変数に変数を割り当てて、毎回新しい変数を取得できるようにします。

foreach( var keyword in keywords )
{
    var kwd = keyword;
    stories = from story in stories where story.Name.Contains ( kwd ) );
}

Eric Lippertは、クロージャにループ変数を含めることの危険性とそれを回避する方法を説明する優れた記事(または2つ)を持っています。

于 2009-12-21T16:22:57.030 に答える
1

非常に簡単な方法は次のとおりです。

var keywords = new [] { "alpha", "bravo", "charlie" };
IQueryable<Story> stories = DataContext.Stories;

foreach( var keyword in keywords )
{
    string kw = keyword;
    stories = from story in stories where story.Name.Contains ( kw ) );
}

return stories;

あなたも考えることができます

var keywords = new [] { "alpha", "bravo", "charlie" };
IQueryable<Story> stories = DataContext.Stories
                                       .Where(story => keywords.All(kw => story.Name.Contains(kw));
于 2009-12-21T16:23:00.693 に答える
1

キーワードのローカル コピーを作成する必要があります。

foreach( var keyword in keywords )
{
    var localKeyword = keyword;
    stories = from story in stories where story.Name.Contains ( localKeyword ) );
}
于 2009-12-21T16:24:02.283 に答える
0

問題の一部を解決しました。「変更されたクロージャへのアクセス」は、次のように、キーワード変数のローカルスコープのコピーで簡単に修正できます。

var keywords = new [] { "alpha", "bravo", "charlie" };
IQueryable<Story> stories = DataContext.Stories;

foreach( var keyword in keywords )
{
    var innerKeyword = keyword;
    stories = from story in stories where story.Name.Contains ( innerKeyword ) );
}

return stories;

残念ながら、複数のwhere句を追加してもうまくいかない場合があります。式がANDで区切られている各LINQであるため、すべてのキーワードを満たすストーリーのみが返されます。いずれかのキーワードのストーリーが欲しいです。

于 2009-12-21T16:28:35.710 に答える
0

試したことはありませんが、このようなことはできますか?

from story in stories
where keywords.All(kw => story.Name.Contains(kw));
于 2009-12-21T16:30:44.877 に答える
0

編集:これは正しくありません。以下のコメントを参照してください。

フォローアップの回答で示したように、すべてのキーワードではなく 1 つ以上のキーワードに一致するストーリーを検索する場合は、Any 演算子を使用する必要があります。次のクエリ(テストされていません)が機能すると思います:

IQueryable<Story> matchingStories = 
    from story in stories
    where keywords.Any(keyword => story.Name.Contains(keyword));
于 2009-12-21T16:40:42.407 に答える