Запрос Linq, построенный в цикле foreach, всегда принимает значение параметра из последней итерации
У меня есть список, содержащий несколько ключевых слов.
Я прошу через них построить мой 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 как тем же (последним итерированным) значением.
Что я делаю неправильно?
Ответы
Ответ 1
Вы повторно используете одну и ту же переменную (key
) в выражении лямбда.
Более подробную информацию см. в моей статье о анонимных методах, а также есть ряд связанных вопросов SO:
Простое исправление состоит в том, чтобы сначала скопировать переменную:
List<string> keys = FillKeys()
foreach (string key in keys){
string copy = key;
q = q.Where(c => c.Company.Name.Contains(copy));
}
Ответ 2
Возможно, проблема с захваченной переменной; попробуйте добавить:
List<string> keys = FillKeys()
foreach (string key in keys){
string tmp = key;
q = q.Where(c => c.Company.Name.Contains(tmp));
}
Ответ 3
он был исправлен в С# 5.0, а пример выше в С# 5.0 работает, но не работает в более ранних версиях С#.
Но будьте осторожны, это не касается цикла 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);
}