2008-10-10 5 views
11

Angenommen, meine Objekte funktionieren einwandfrei (d. H. TDD lässt mich glauben, dass sie funktionieren).Erstellen einer LINQ-Abfrage programmgesteuert, ohne dass lokale Variablen mich austricksen

Ich habe eine Liste, die ich so erstellen (außer eingekerbten richtig):

var result = from v in vendors 
      from p in v.Products 
      orderby p.Name 
      select p; 

Das funktioniert - ich alle Produkte aller Anbieter.

Jetzt habe ich eine Liste von Bedingungen, die zur Laufzeit vom Benutzer aufgebaut werden. Lassen Sie uns sie anwenden:

foreach (Attribute a in requiredAttributes) 
{ 
    result = result.Where(p => p.Attributes.Contains(a)); 
} 

Dies kann primitiv sein, aber ich dachte, es würde funktionieren. Nachdem diese foreach-Schleife beendet ist, wenn Sie "Ergebnis" aufzählen, wird es jedoch alle Produkte enthalten, die über das Attribut LAST der requiredAttributes-Auflistung in seiner Attributeigenschaft verfügen (auch eine Auflistung).

Für mich wird dieser Geruch wie "a" irgendwo bei jedem Trip durch die Schleife überschrieben, und nur der letzte gilt.

Kurz von irgendwie schreiben eine Erweiterungs-Methode zu IEnumerable namens ContainsAll (IEnumerable) oder etwas zu diesem Zweck, wie kann ich erreichen, was ich will, das ist im Grunde eine logische UND, mir nur die Produkte, die alle erforderlichen Attribute hat ?

+0

Danke Jungs - Ich gebe Jon die "akzeptierte Antwort", da es scheint, dass es für den "allgemeinen" Fall besser geeignet wäre. Aber ich werde auf jeden Fall Omers Vorschlag überprüfen, wusste nicht, dass Sie das tun könnten. Stimmen für alle. :) –

+0

Der neue Weg, um dieses Problem zu beheben, ist nur C# 5 zu verwenden. Siehe auch [Wurde foreachs Verwendung von Variablen in C# 5 geändert?] (Http://stackoverflow.com/a/12112959/18192) – Brian

Antwort

19

(Edited Klarheit halber.)

Das Problem ist die foreach Schleife, und die Tatsache, dass die „a“ Variable und dann jedes Mal geändert wird, erfasst wird. Hier ist eine Modifikation, die funktioniert, indem Sie effektiv eine "neue" Variable für jede Iteration der Schleife einführen und diese neue Variable erfassen.

foreach (Attribute a in requiredAttributes) 
{ 
    Attribute copy = a; 
    result = result.Where(p => p.Attributes.Contains(copy)); 
} 

Omer-Lösung ist ein Reiniger ein, wenn Sie es verwenden können, aber dies kann helfen, wenn Ihr richtiger Code tatsächlich komplizierter ist :)

EDIT: Es gibt mehr über das Thema in this closures article - nach unten scrollen zu "Vergleich der Erfassungsstrategien: Komplexität vs. Leistung".

+0

Ich habe diese kritische Nuance von ReSharper's verwirrenden Warnungen darüber erfahren;) – Grank

7
var result = from v in vendors 
      from p in v.Products 
      where requiredAttributes.All(a => p.Attributes.Contains(a)) 
      orderby p.Name 
      select p; 

HTH.

5

Ich habe es nicht codiert, sondern

foreach (Attribute a in requiredAttributes){  
    result = result.Where(p => p.Attributes.Contains(a)); 
} 

zu

foreach (Attribute a in requiredAttributes){  
    Attribute b = a; 
    result = result.Where(p => p.Attributes.Contains(b)); 
} 

sollte es beheben ändern, denke ich.