2010-12-02 9 views
1

Ich habe Schwierigkeiten mit IEnumerable und LINQ. Vielleicht verstehe ich es nicht vollständig.Schwierigkeit mit LINQ

Ich habe eine Datenquelle von gefälschten Dateien ("PlumFile") und einige Filter. Jeder Filter hat eine Fits(PlumFile)10, die bestimmt, ob das Argument zu diesem Filter passt. Jeder Filter hat auch eine Aufzählung, die anzeigt, ob es "und", "oder" oder "nicht" ist.

Hier ist, wie ich versuche, all diese Filter in einer Abfrage zu kombinieren:

public ObservableCollection<PlumFile> FoundFiles 
    { 
     get 
     { 
      ObservableCollection<PlumFile> searchResults = new ObservableCollection<PlumFile>(); 

      // get the data source 
      IEnumerable<PlumFile> query = PlumData.GetFiles(); 

      foreach (FilterConstraint filter in filters) 
      { 
       // debugging 
       IList<PlumFile> oldQuery = query.ToList(); 

       switch (filter.QueryCombiningRule) 
       { 
        case FilterConstraint.QueryRule.And: 
         query = query.Where(file => filter.Fits(file)); 
         break; 
        case FilterConstraint.QueryRule.Or: 
         query = query.Concat(PlumData.GetFiles().Where(file => filter.Fits(file))); 
         break; 

        // is this really how I want to do 'not'? 
        case FilterConstraint.QueryRule.Not: 
         query = query.Where(file => !filter.Fits(file)); 
         break; 
       } 

       // debugging 
       IList<PlumFile> currQuery = query.ToList(); 

      } 

      query = query.Distinct(); 

      foreach (PlumFile file in query) 
      { 
       searchResults.Add(file); 
      } 

      return searchResults; 
     } 
    } 

Ich bin mir nicht sicher, was ich falsch mache. Für einige Abfragen funktioniert es gut. Für andere versagt es.

Wenn ich einen einzelnen "und" Filter habe, funktioniert es gut. Dann füge ich einen "nicht" -Filter hinzu, der nichts bereits Ausgewähltes herausfiltern sollte, aber alles wird entfernt. Warum ist das?

(Ich tue dies für eine Silverlight 4-Anwendung, aber ich glaube nicht, dass es wichtig ist.)

aktualisieren: Ein Beispiel für eine Filterbeschränkung:

public class NameFilterConstraint : FilterConstraint 
{ 
    public string Name { get; set; } 

    public override bool Fits(PlumFile plumFile) 
    { 
     return plumFile.Name.Contains(Name); 
    } 

    public override string Description 
    { 
     get 
     { 
      return ToString(); 
     } 
    } 

    public override string ToString() 
    { 
     return String.Format("Name contains '{0}'", Name); 
    } 
} 

Update 2: Hier ist eine nicht-LINQ-Version, die nicht die Fehler hat bereits erwähnt:

public ObservableCollection<PlumFile> FoundFiles 
     { 
      get 
      { 
       ObservableCollection<PlumFile> searchResults = new ObservableCollection<PlumFile>(PlumData.GetFiles().ToList()); 

       foreach (FilterConstraint filter in filters) 
       { 
        switch (filter.QueryCombiningRule) 
        { 
         case FilterConstraint.QueryRule.And: 
          foreach (PlumFile file in searchResults.ToList()) 
          { 
           if (! filter.Fits(file)) 
           { 
            searchResults.Remove(file); 
           } 
          } 
          break; 

         case FilterConstraint.QueryRule.Or: 
          foreach (PlumFile file in PlumData.GetFiles()) 
          { 
           if (filter.Fits(file)) 
           { 
            searchResults.Add(file); 
           } 
          } 
          break; 

         case FilterConstraint.QueryRule.Not: 
          foreach (PlumFile file in searchResults.ToList()) 
          { 
           if (filter.Fits(file)) 
           { 
            searchResults.Remove(file); 
           } 
          } 
          break; 
        } 
       } 

       return new ObservableCollection<PlumFile>(searchResults.Distinct()); 
      } 
     } 

Also, ich denke mein Problem ist gelöst, obwohl ich immer noch neugierig bin, was ich falsch mit dem LINQ mache. Vielleicht wurden meine Absichten (die im letzten Beispiel deutlich gemacht wurden) nicht richtig in LINQ übersetzt?

+1

Können Sie Ihren Code für die Methode "Fits" hinzufügen? – Mark

+0

Ich verbrachte 5 Minuten damit, das zu starren, aber mein Gehirn tut weh, wenn ich nur die Frage ansehe, wie sie aussieht :) – Contango

+0

@Gravitas: Ist das, weil ich LINQ missbrauche? Wie kann ich das weniger kompliziert machen? –

Antwort

2

Nur eine Vermutung hier, aber ich frage mich, ob dies ein Capture/Closure-Problem ist. Versuchen Sie dies:

+0

Wie würde das einen Unterschied machen? –

+0

Probieren Sie es zuerst und wenn ja, erkläre ich warum. – BFree

+0

@Rosarch: Die Lambda-Ausdrücke in den Where-Klauseln verweisen auf die Filterloop-Variable. Der Compiler wird einen Verweis auf die Filterloopvariable erfassen, aber nicht ihren Wert zum Zeitpunkt der Referenz. Wenn diese Lambdas später ausgewertet werden (LINQ Lazy Eval, typischerweise bei der nächsten Umwandlung ToList()), wird die Filterschleifenvariable wahrscheinlich anders sein als wenn die Referenz gemacht wurde - die Schleife ist weitergezogen. Wenn Sie die Schleifenvariable einer lokalen Variablen zuweisen und die lokale Variable in den Lambda-Variablen verwenden, muss der Compiler den aktuellen Wert und nicht nur eine Referenz auf die Schleifenvariable erfassen. – dthorpe

1

Auf den ersten Blick würde ich vorschlagen, die Filter als eifrige Bewertung (nicht faul) anwenden.

Verwenden Sie .ToList() nach dem Anwenden aller Filter innerhalb der Schleife + make "Abfrage" -Liste. Wenn das nicht funktioniert, müssen Sie auch die "Passt" -Methode posten.

+0

Ich postete ein 'Passt()' –