2017-05-15 3 views
0

Ich habe eine Liste von Objekten des Typs Foo und eine andere Instanz eines Objekts vom Typ Foo. Ich möchte Linq verwenden, um die Liste basierend auf den Nicht-Null-Eigenschaften der Instanz zu filtern.Linq: Filterliste des Typs A basierend auf einem Objekt des Typs A?

class Foo { 
    public int ID; 
    public string Description; 
    public long Location; 
} 

Foo fooFilter = new Foo() { 
    ID = null, 
    Description = null, 
    Location = 1 
} 

List<Foo> fooList = new List<Foo>(); 

fooList.Add(new Foo(){ID = 1, Description = "one", Location = 1}); 
fooList.Add(new Foo(){ID = 2, Description = "two", Location = 0}); 
fooList.Add(new Foo(){ID = 3, Description = "three", Location = 1}); 

List<Foo> filteredFooList = fooList.Where(???); 

ich irgendwie wollen fooFilter verwenden fooList abzufragen und füllen filteredFooList mit:

[ 
    {ID = 1, Description = "one", Location = 1}, 
    {ID = 3, Description = "three", Location = 1} 
] 

EDIT:

ich versuchte, sich kurz zu fassen die Frage klarer zu machen, aber Ich könnte wichtige Informationen verlassen haben. In meinem aktuellen Programm ist List<Foo> ein großes Ergebnis aus einer Datenbank (über 40k Einträge). Ich versuche, eine Controller-Methode (MVC) zu machen, die eine beliebige Kombination von Parametern annehmen kann, die den Feldnamen eines Entity-Framework-Objekts entsprechen. So ist <Foo> ein EF-Record-Typ. Also versuche ich ausdrücklich zu vermeiden, dass alle aufzulisten, die (15 oder so) Felder, die in der Steuerung gefiltert schaltet werden kann:

public class Home : Controller 
{ 
    public ActionResult FilteredFooList(int ID, string Description, long Location, etc, etc, etc) 
    { 

    } 
} 

und etwas mehr wie:

public class Home : Controller 
{ 
    public ActionResult FilteredFooList(Foo filterObj) 
    { 

    } 
} 

Vielleicht ist nicht möglich oder eine gute Idee?

+0

Ihr fooFilter ist kein Filter, sondern ein Foo-Objekt. Ein Filterprädikat ist ein Ausdruck, der entweder wahr oder falsch ist, richtig? – elgonzo

+1

'.ID' und' .Location' können nicht 'null' sein, da sie Werttypen sind. Wie planen Sie mit Werttypen umzugehen? – NetMage

+0

True, 'fooFilter' ist ein Beispiel für eine Abfrage mit Beispiel, wie 'System.DirectoryServices.AccountManagement.PrincipalSearcher' funktioniert. – NetMage

Antwort

1

Sie sich nicht in eine Ecke malen, indem unnötige Objekte zu Ihren Datenobjekte hinzufügen, sollten sie Datenobjekte bleiben. Sie versuchen effektiv, eine dynamische Abfrage zu erstellen, bei der Sie nach einer Liste von Bedingungen filtern möchten. Dafür gibt es Muster.

Beginnen Sie mit einer Basisabfrage und bestimmen Sie dann, ob Sie nach einer der Eigenschaften filtern möchten. Machen Sie das Gleiche mit dem Rest der anderen Eigenschaften. Wenn Sie das Ende erreicht haben, können Sie dann einfach die Ergebnisse sammeln.

var filter = new Foo 
{ 
    ID = null, 
    Description = null, 
    Location = 1, 
}; 

var data = new List<Foo> 
{ 
    new Foo { ID = 1, Description = "one", Location = 1 }, 
    new Foo { ID = 2, Description = "two", Location = 0 }, 
    new Foo { ID = 3, Description = "three", Location = 1 }, 
}; 

var query = data.AsEnumerable(); 
if (filter.ID != null) 
    query = query.Where(x => x.ID == filter.ID); 
if (filter.Description != null) 
    query = query.Where(x => x.Description == filter.Description); 
if (filter.Location != null) 
    query = query.Where(x => x.Location == filter.Location); 

var result = query.ToList(); 

Dies setzt voraus, dass ID und Location tatsächlich nullable sind wie Ihr Beispiel impliziert.

public class Foo 
{ 
    public int? ID { get; set; } 
    public string Description { get; set; } 
    public long? Location { get; set; } 
} 
+0

In Meine Situation, 'List ' ist eigentlich eine ziemlich große Liste (Zehntausende) aus einer Datenbank. Wäre das noch angemessen? – AnalogWeapon

+0

Ja absolut, noch besser, da Sie die Abfrage erstellen und auf dem Server ausführen können, anstatt alles als eine Liste, _then_ Filterung zu erhalten. Im Allgemeinen sollten Sie versuchen, es so zu strukturieren, dass es wann immer möglich auf dem Server ausgeführt wird. –

0

Vorausgesetzt, dass Sie Ihre Werttypen ändern kann sein nullable:

class Foo { 
    public int? ID; 
    public string Description; 
    public long? Location; 
} 

Dann können Sie einige Erweiterungen verwenden:

public static class Ext { 
    public static bool EqualOrNull<T>(this T? value, T? filter) where T : struct, IComparable { 
     return (filter == null) || (value.Value.CompareTo(filter.Value) == 0); 
    } 
    public static bool EqualOrNull<T>(this T value, T filter) where T : class, IComparable { 
     return (filter == null) || (value.CompareTo(filter) == 0); 
    } 
} 

dies zu tun:

var filteredFooList = fooList.Where(f => f.ID.EqualOrNull(fooFilter.ID) && f.Description.EqualOrNull(fooFilter.Description) && f.Location.EqualOrNull(fooFilter.Location)); 

Wenn Sie wollten etwas wirklich Generisches (z. B. nicht abhängig von der Kenntnis der Feldnamen) müssten Sie in die Welt von Refl treten Ektion.

2

Wenn Sie nur nicht-instanziierte Eigenschaften herausfiltern möchten, brauchen Sie keinen Filter.

class Foo 
{ 
    public int ID; 
    public string Description; 
    public long Location; 

    public bool IsInstanciated() 
    { 
     return this.ID != default(int) && this.Description != default(string) && this.Location != default(long); 
    } 
} 

List<Foo> filteredFooList = fooList.Where(f => f.IsInstanciated()); 

Edit: Wenn Sie wirklich, dass instanziiert Klasse als Filter verwenden müssen, würde ich vorschlagen, dass Sie IEquatable<T>

class Foo : IEquatable<Foo> 
{ 
    public int ID; 
    public string Description; 
    public long Location; 

    public bool Equals(Foo other) 
    { 
     // Whatever your logic is 
     return string.IsNullOrEmpty(this.Description) == string.IsNullOrEmpty(other.Description) && 
       this.ID > 0 == other.ID > 0 && 
       this.Location > 0 == other.Location > 0; 
    } 
} 

public class Home : Controller 
{ 
    public ActionResult FilteredFooList(Foo filterObj) 
    { 
     List<Foo> filteredFooList = fooList.Where(f => f.Equals(filterObj)); 
    } 
} 
+1

Sie sollten erwähnen, dass man in diesem Fall * auch * die Methode 'Equals (object) 'implementieren sollte, um unbeabsichtigte Ergebnisse zu vermeiden, wenn' Foo.Equals ((object) OtherFoo) 'aufgerufen wurde, usw. –

0

von Ihnen erwartete Ausgabe der Suche zu verwenden, ist es scheint, dass Sie fooList filtern möchten, damit Sie alle Objekte erhalten, die das gleiche Location wie Ihr fooFilter Objekt haben. Wenn das, was Sie fragen, können Sie dies tun:

List<Foo> filteredFooList = fooList.Where(item => item.Location == fooFilter.Location); 
0

Ich löste vor kurzem ein sehr ähnliches Problem, indem dynamisch die Lambda-Abfrage basierend auf Eingabeobjekten mit hauptsächlich Expression Klasse in .net aufzubauen. Ich kann eine Lösung aufschreiben, wenn Sie sich für Ihren Fall interessieren, nur vorsichtig sein, dies zu tun, da bereits eine Antwort akzeptiert ist, aber auf mögliche Alternativen hingewiesen wird.

Verwandte Themen