3

EF 5を使用して、複数の検索条件を結果セットに適用しようとしています(この場合、ライブラリカタログ検索の場合)。関連するコードは次のとおりです。

public IQueryable<LibraryResource> GetSearchResults(string SearchCriteria, int? limit = null)
    {
        List<string> criteria = SearchCriteria.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();
        IQueryable<LibraryResource> allResults = context.LibraryResources.Include("Type").Where(r => r.AuditInfo.DeletedAt == null);
        foreach (string criterion in criteria)
        {
            allResults = allResults.Where(r => (r.Title.Contains(criterion) || r.Keywords.Contains(criterion) || r.Author.Contains(criterion) || r.Comments.Contains(criterion)));
        }
        allResults = allResults.OrderBy(r => r.Title);
        if (limit.HasValue) allResults = allResults.Take(limit.Value);
        return allResults;
    }

サンプルSearchCriteria="歴史の時代"

何らかの理由で、最後の基準のみが適用されます。たとえば、上記のサンプルでは、​​タイトル、著者、キーワード、コメントに「時代」が含まれるすべての書籍が、「履歴」でフィルタリングせずに返されます。コードをステップスルーすると、ループは2回実行され、毎回適切な基準が適用されます。見えないものが見えますか?ありがとう!

4

1 に答える 1

5

クローズドオーバー変数の値を変更する犠牲になりました。

コードを次のように変更します。

foreach (string criterion in criteria)
{
    var crit = criterion;
    allResults = allResults.Where(/* use crit here, not criterion */);
}

ここでの問題は、クエリを作成している間、フィルタリング式が変数criterionに近づき、実際には、クエリが評価されるポイントでスコープ内にプルされることです。ただし、その時点では値は1つcriterion(ループオーバーした最後の値)しかないため、最後のフィルターを除くすべてのフィルターが実際には最後のフィルターの複製になります。

のローカルコピーを作成し、criterionそれを式の内部で参照すると、問題が修正されます。これは、ループの1回の反復から次の反復まで延長されない存続期間を持つ、毎回異なるcritローカル変数であるためです。

詳細については、読みたいと思うかもしれません。C#がforeachで変数を再利用する理由はありますか?、C#5.0は、このシナリオに適用される重大な変更を行うことにも言及されています。ループ変数の有効期間がcriterion変更され、このコードが余分なローカルなしで正しく機能するようになります。

于 2013-02-04T21:06:28.257 に答える