2010-05-06 9 views
9

Ich habe eine Schnittstelle namens ICatalog wie unten gezeigt, wo jeder ICatalog hat einen Namen und eine Methode, die Elemente basierend auf einer Predicate<Item> Funktion zurückgibt.Linq basierte generische Alternative zu Prädikat <T>?

Eine bestimmte Implementierung eines Katalogs kann mit Katalogen in verschiedenen Formaten wie XML oder einer SQL-Datenbank verknüpft werden.

Mit einem XML-Katalog schließe ich die gesamte XML-Datei in den Speicher deserialisieren, so dass das Testen jedes Elements mit der Prädikatfunktion nicht viel mehr Overhead hinzufügt, als es bereits im Speicher ist.

Noch mit der SQL-Implementierung würde ich lieber nicht den gesamten Inhalt der Datenbank in den Speicher abrufen und dann die Elemente mit der Prädikatfunktion filtern. Stattdessen möchte ich eine Möglichkeit finden, das Prädikat irgendwie an den SQL-Server zu übergeben oder es irgendwie in eine SQL-Abfrage umzuwandeln.

Dies scheint wie ein Problem, das mit Linq gelöst werden kann, aber ich bin ziemlich neu. Sollte meine Schnittstelle stattdessen IQueryable zurückgeben? Im Moment mache ich mir keine Gedanken darüber, wie ich tatsächlich eine SQL-Version meines ICatalogs implementieren kann. Ich möchte nur sicherstellen, dass meine Schnittstelle es in Zukunft erlauben wird.

Antwort

7

Rob hat angegeben, wie Sie dies tun könnten (obwohl ein klassischerer LINQ-Ansatz möglicherweise Expression<Func<Item,bool>> und möglicherweise IQueryable<IFamily> zurückgibt).

Die gute Nachricht ist, dass wenn man das Prädikat mit LINQ-to-Objects (für XML-Szenario) verwenden möchten, können Sie dann benutzen Sie einfach:

Predicate<Item> func = predicate.Compile(); 

oder (für die andere Signatur):

Func<Item,bool> func = predicate.Compile(); 

und Sie haben einen Delegaten (func), um Ihre Objekte mit zu testen. obwohl

Das Problem ist, dass dies ein Alptraum zu Unit-Test - kann man nur wirklich Integration Test ihm.

Das Problem ist, dass Sie nicht zuverlässig (mit LINQ-to-Objects) alles, was komplexe Datenspeicher betrifft, vortäuschen können; zum Beispiel wird die folgende Arbeit in Ihrer Unit-Tests gut, aber wird nicht funktionieren „für echte“ gegen eine Datenbank:

var foo = GetItems(x => SomeMagicFunction(x.Name)); 

static bool SomeMagicFunction(string name) { return name.Length > 3; } // why not 

Das Problem ist, dass nur einige Operationen TSQL übersetzt werden kann.Sie erhalten das gleiche Problem mit IQueryable<T> - beispielsweise unterstützen EF und LINQ-to-SQL verschiedene Operationen für eine Abfrage. auch nur First() verhält sich anders (EF erfordert, dass Sie es zuerst explizit bestellen, LINQ-to-SQL nicht).

Also zusammenfassend:

  • kann es
  • arbeiten, aber denken Sie sorgfältig, ob Sie dies tun wollen; eine klassische Black-Box-Repository/Service-Schnittstelle
+0

Danke für die Antwort Marc. Hört sich komplizierter an, als ich es für das, was ich versuche, gerne hätte. Was meinst du mit einer "klassischen Black Box Repository/Service-Schnittstelle"? Können Sie ein Beispiel geben? –

5

Sie brauchen nicht den ganzen Weg zu gehen, und erstellen Sie eine IQueryable implementation

Wenn Sie Ihre GetItems Methode als erklären:

IEnumerable<IFamily> GetItems(Expression<Predicate<Item>> predicate); 

Dann können Sie Ihre implementierende Klasse die Expression untersuchen, um zu bestimmen, was sein wird, fragte.

Lassen Sie sich den Artikel IQueryable durchlesen, da er erklärt, wie Sie einen Ausdrucksbaum-Besucher erstellen, den Sie benötigen, um eine einfache Version von.

+0

FYI mehr prüfbar sein kann: Ich habe das Original Beispiel verkorkste soll es sowohl beide IEnumerable ein verwendet und Prädikat nicht IEnumerable