Запрос Linq, встроенный в цикл foreach, всегда принимает значение параметра из последней итерации - PullRequest
17 голосов
/ 17 ноября 2008

У меня есть список, содержащий несколько ключевых слов. Я использую их для создания моего linq-запроса следующим образом (сводится к удалению шума кода):

List<string> keys = FillKeys()
foreach (string key in keys){
    q = q.Where(c => c.Company.Name.Contains(key));
}

Когда я теперь заставляю мои ключи содержать 2 ключа, которые возвращают результаты по отдельности, но никогда не могут возникать вместе (каждый элемент в q - это либо «xyz», либо «123», но не «123» И «xyz»), я все равно получаю Результаты. Набор результатов будет таким же, как и последняя строка, к которой он пришел.

Я посмотрел на запрос linq и, похоже, он создает правильный sql, но он заменяет @ p1 AND @ p2 на одно и то же (последнее итерированное) значение.

Что я делаю не так?

Ответы [ 3 ]

33 голосов
/ 17 ноября 2008

Вы повторно используете ту же самую переменную (key) в своем лямбда-выражении.

См. Мою статью о анонимных методах для получения более подробной информации, а также есть ряд связанных с этим вопросов SO:

Простое исправление - сначала скопировать переменную:

List<string> keys = FillKeys()
foreach (string key in keys){
    string copy = key;
    q = q.Where(c => c.Company.Name.Contains(copy));
}
14 голосов
/ 17 ноября 2008

Возможно захваченная переменная проблема; попробуйте добавить:

List<string> keys = FillKeys()
foreach (string key in keys){
    string tmp = key;
    q = q.Where(c => c.Company.Name.Contains(tmp));
}
1 голос
/ 30 декабря 2012

это было исправлено в C # 5.0, и приведенный выше пример в C # 5.0 работает, но не работает в более ранних версиях C #.

Но будьте осторожны, это не касается цикла for

  static void Main()
        {
            IEnumerable<char> query = "aaa bbb ccc";
            string lettersToRemove = "ab";

            Console.WriteLine("\nOK with foreach:");
            foreach (var item in lettersToRemove)
            {
                query = query.Where(c => c != item);   
            }   
            foreach (char c in query) Console.Write(c);

            //OK:
            Console.WriteLine("\nOK with foreach and local temp variable:");
            query = "aaa bbb ccc";

            foreach (var item in lettersToRemove)
            {
                var tmp = item;
                query = query.Where(c => c != tmp);
            }            
            foreach (char c in query) Console.Write(c);


            /*
             An IndexOutOfRangeException is thrown because:
             firstly compiler iterates the for loop treating i as an outsite declared variable  
             when the query is finnaly invoked the same variable of i is captured (lettersToRemove[i] equals 3) which generates IndexOutOfRangeException

             The following program writes aaa ccc instead of writing ccc:
             Each iteration gets the same variable="C", i (last one frome abc). 
             */

            //Console.WriteLine("\nNOK with for loop and without temp variable:");
            //query = "aaa bbb ccc";

            //for (int i = 0; i <  lettersToRemove.Length; i++)
            //{
            //    query = query.Where(c => c != lettersToRemove[i]);
            //}
            //foreach (char c in query) Console.Write(c);

            /*
             OK
             The solution is to assign the iteration variable to a local variable scoped inside the loop
             This causes the closure to capture a different variable on each iteration.
            */
            Console.WriteLine("\nOK with for loop and with temp variable:");
            query = "aaa bbb ccc";

            for (int i = 0; i < lettersToRemove.Length; i++)
            {
                var tmp = lettersToRemove[i];
                query = query.Where(c => c != tmp);
            }
            foreach (char c in query) Console.Write(c);
        } 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...