2008-11-17 7 views
15

Mam listę zawierającą kilka słów kluczowych. I foreach przez nich budowy mój kwerendy LINQ z nich jak tak (sprowadzić do usuwania szumu kierunkowym):Zapytanie Linq wbudowane w pętlę foreach zawsze przyjmuje wartość parametru z ostatniej iteracji

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

Kiedy teraz uczynić moje klucze zawierają 2 klucze, które zwracają wyniki oddzielnie, ale nigdy nie może wystąpić maksymalnie razem (każda pozycja w q jest albo "xyz" albo "123", nigdy "123" i "xyz"), wciąż otrzymuję wyniki. Zestaw wyników jest taki sam jak ostatni ciąg znaków, do którego dotarł.

Zerknąłem na zapytanie linq i wygląda na to, że tworzy poprawny sql, ale zastępuje @ p1 AND @ p2 zarówno tą samą (ostatnią iterowaną) wartością.

Co robię źle?

+0

możliwe duplikat [C# Zrobione zmiennej w pętli] (http://stackoverflow.com/questions/271440/c-sharp-captured- variable-in-loop) – nawfal

Odpowiedz

31

Używasz tej samej zmiennej (key) w wyrażeniu lambda.

Zobacz mój artykuł na anonymous methods więcej szczegółów, i istnieje wiele powiązanych SO pytania zbyt:

Prostym rozwiązaniem jest skopiować pierwszą zmienną:

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

Hehe, znalazł to w międzyczasie: http://stackoverflow.com/questions/190227/building-a-linq-query-programically-without-local-variables-tricking-me Easy (ale zarobione) kredyty dla ciebie;) –

+0

znowu mnie oszukali ... –

12

Ewentualnie schwytany zmienna kwestia; spróbuj dodać:

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

Dzięki. Zasadniczo to samo co skeet, ale mogę przypisać tylko jedno rozwiązanie. Wskazuje jednak. –

+1

Nie ma problemu. Uważam to za uspokajające, że daliśmy tę samą odpowiedź ;-p –

1

zostało to naprawione w C# 5.0, a powyższy przykład w C# 5.0 działa, ale kończy się niepowodzeniem we wcześniejszych wersjach C#.

Ale uważaj, nie dotyczy pętli 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); 
     }