3

クエリ可能なアイテムを検索するために書いた関数の単体テストの最中です。メソッドが機能する場合は、1 つのアイテムが返されると主張するだけです。しかし、私は0アイテムを返します。私のメソッドでは、遅延実行を使用し、ToListそれを返す前に使用します。しかし、リストを直接操作するようにメソッドを変更し、 を繰り返し呼び出すとToList、正しい結果が得られます。

遅延実行が即時実行と同じ結果をもたらすと仮定するのは安全ではないと言うのは正しいですか?

これは、0 アイテムを返すことを示すための小さなアプリです。

   class Program
    {

        static void Main(string[] args)
        {
            Dictionary<string, string> values = new Dictionary<string, string>()
            {
                 {
                     "Prop1",
                     "*Value*"
                 },
                 {
                     "Prop2",
                     "2*"
                 }
            };
            List<InputItem> items =new List<InputItem>()
            {
                new InputItem()
            };
            Console.WriteLine(Helper.SearchInputItems(items.AsQueryable(), values).Count);
            Console.ReadLine();
        }
    }

    public class InputItem
    {
        public Dictionary<string, string> MappedValues = new Dictionary<string, string>()
        {
            {
                     "Prop1",
                     "This is a value that should be found"
                 },
                 {
                     "Prop2",
                     "2 everything that begins with 2 should be found"
                 }
        };
    }
    public static class Helper
    {
        delegate bool Searcher(string input, string searchString);
        /// <summary>
        /// Searches the added input items.
        /// </summary>
        /// <param name="values">A dictionary of field names and the search pattern for that field.</param>
        /// <returns>List of found InputItems.</returns>
        public static List<InputItem> SearchInputItems(IQueryable<InputItem> inputItems, Dictionary<string, string> values)
        {
            foreach (var value in values)
            {
                string searchString = value.Value;
                Searcher searcher;
                if (searchString.StartsWith("*") && searchString.EndsWith("*"))
                {
                    searcher = new Searcher(StringHelpers.Contains);
                    searchString = searchString.Substring(1);
                    searchString = searchString.Remove(searchString.Length - 1);
                }
                else if (searchString.EndsWith("*"))
                {
                    searcher = new Searcher(StringHelpers.StartsWith);
                    searchString = searchString.Remove(searchString.Length - 1);
                }
                else
                {
                    searcher = new Searcher(StringHelpers.Exact);
                }
                inputItems = inputItems.Where(c =>
                    c.MappedValues.Any(x => x.Key == value.Key) &&
                    searcher(c.MappedValues.First(x => x.Key == value.Key).Value, searchString)
                    );

            }
            return inputItems.ToList();
        }
    }
    public static class StringHelpers
    {
        public static bool Contains(string input, string searchString)
        {
            return input.ToUpperInvariant().Contains(searchString.ToUpperInvariant());
        }
        public static bool StartsWith(string input, string searchString)
        {
            return input.ToUpperInvariant().StartsWith(searchString.ToUpperInvariant());
        }
        public static bool Exact(string input, string searchString)
        {
            return input.ToUpperInvariant() == searchString.ToUpperInvariant();
        }
    }

2 everything that begins with 2 should be foundブレークポイントを設定すると、が含まれているかどうかを実際に確認できますがValue、そうではなく、false を返します。そのため、where句のFirstOrDefaultが間違ったアイテムを選択しているようです

4

1 に答える 1

3

わかりました。これは、ループ変数をキャプチャするという古い問題です。

ここ:

        foreach (var value in values)
        {
            ...
            inputItems = inputItems.Where(c =>
                c.MappedValues.Any(x => x.Key == value.Key) &&
                searcher(c.MappedValues.First(x => x.Key == value.Key).Value, 
                                              searchString)
                );

        }

valueラムダ式内で使用しています。valueつまり、実行時に「の現在の値」が使用され、ループが繰り返されるとその値が変化します。

単純に使用します:

foreach (var valueIterationVariable in values)
{
    var value = valueIterationVariable;
    // code as before
}

大丈夫だと信じています。(ちなみに、「値」という名前の使用について質問しますが、それは別の問題です。)

なぜそれが で機能したのかを深く調べていませIEnumerable<T>IQueryable<T>が、余分な延期が原因だったのではないかと思います。

于 2011-08-11T09:37:23.687 に答える