Ich habe viele Projekte mit Repositorys gesehen, von denen Instanzen zurückgegeben werden IQueryable
. Auf diese Weise können zusätzliche Filter und Sortierungen für den IQueryable
von anderen Code ausgeführt werden, wodurch andere SQL-Anweisungen generiert werden. Ich bin gespannt, woher dieses Muster stammt und ob es eine gute Idee ist.
Meine größte Sorge ist, dass IQueryable
es ein Versprechen ist, die Datenbank einige Zeit später zu erreichen, wenn sie aufgelistet wird. Dies bedeutet, dass ein Fehler außerhalb des Repositorys ausgegeben wird. Dies kann bedeuten, dass eine Entity Framework-Ausnahme in einer anderen Ebene der Anwendung ausgelöst wird.
Ich bin in der Vergangenheit auch auf Probleme mit MARS (Multiple Active Result Sets) gestoßen (insbesondere bei der Verwendung von Transaktionen), und dieser Ansatz klingt so, als würde er dazu führen, dass dies häufiger vorkommt.
Ich habe immer AsEnumerable
oder ToArray
am Ende jedes meiner LINQ-Ausdrücke aufgerufen , um sicherzustellen, dass die Datenbank gefunden wurde, bevor der Repository-Code verlassen wurde.
Ich frage mich, ob die Rückgabe IQueryable
als Baustein für eine Datenschicht nützlich sein könnte. Ich habe einen ziemlich extravaganten Code mit einem Repository gesehen, das ein anderes Repository aufruft, um ein noch größeres zu erstellen IQueryable
.
quelle
where
Klausel eine Klausel hinzufügenIQueryable
, müssen Sie diese Daten nur drahtlos senden , nicht die gesamte Ergebnismenge.Antworten:
Die Rückgabe von IQueryable bietet den Konsumenten des Repositorys definitiv mehr Flexibilität. Es überträgt die Verantwortung für die Einschränkung der Ergebnisse auf den Kunden, was natürlich sowohl ein Vorteil als auch eine Krücke sein kann.
Auf der anderen Seite müssen Sie nicht viele Repository-Methoden erstellen (zumindest auf dieser Ebene) - GetAllActiveItems, GetAllNonActiveItems usw. -, um die gewünschten Daten zu erhalten. Je nach Ihren Vorlieben kann dies wiederum gut oder schlecht sein. Sie werden (/ sollten) Verhaltensverträge definieren müssen, an die sich Ihre Implementierungen halten, aber wohin das führt, liegt bei Ihnen.
Sie können also die grobkörnige Abruflogik außerhalb des Repositorys platzieren und sie verwenden, wie der Benutzer dies wünscht. So Belichtungs IQueryable gibt die größtmögliche Flexibilität und ermöglicht eine effiziente Abfrage im Gegensatz zu In-Memory - Filterung, usw., und könnte die Notwendigkeit zur Herstellung einer Tonne spezifischen Daten - Abrufverfahren reduzieren.
Andererseits haben Sie Ihren Benutzern jetzt eine Schrotflinte gegeben. Sie können Dinge tun, die Sie möglicherweise nicht beabsichtigt haben (übermäßige Verwendung von .include (), Durchführung schwerer schwerer Abfragen und Durchführung von In-Memory- Filtern in ihren jeweiligen Implementierungen usw.) voller Zugriff.
Abhängig vom Team, der Erfahrung, der Größe der App, der Gesamtschichtung und der Architektur, kommt es also darauf an:
quelle
Expression<Func<Foo,bool>>
an Ihre Methode übergeben. Diese Parameter können direkt an dieWhere
Methode übergeben werden, sodass der Konsument seinen Filter auswählen kann, ohne direkten Zugriff auf IQueryable <Foo> zu benötigen.Es ist keine gute Praxis, IQueryable öffentlichen Schnittstellen auszusetzen. Der Grund ist grundlegend: Sie können IQueryable nicht so realisieren, wie es angeblich ist.
Öffentliche Schnittstelle ist ein Vertrag zwischen Anbieter und Kunden. In den meisten Fällen wird eine vollständige Implementierung erwartet. IQueryable ist ein Cheat:
Das Repository-Muster gibt uns eine klare Darstellung durch Schichtung und vor allem Unabhängigkeit bei der Realisierung. So können wir in Zukunft die Datenquelle ändern.
Die Frage lautet " Sollte die Möglichkeit einer Datenquellensubstitution bestehen? ".
Wenn ein Teil der Daten morgen in SAP-Services, NoSQL-Datenbanken oder einfach in Textdateien verschoben werden kann, können wir eine ordnungsgemäße Implementierung der IQueryable-Schnittstelle gewährleisten?
( mehr gute Punkte, warum dies eine schlechte Praxis ist)
quelle
Realistisch gesehen haben Sie drei Alternativen, wenn Sie eine verzögerte Ausführung wünschen:
IQueryable
.GetCustomersInAlaskaWithChildren
unten)IQueryable
intern erstellt.Ich bevorzuge den dritten (obwohl ich Kollegen auch bei der Implementierung des zweiten geholfen habe), aber es sind offensichtlich einige Einstellungen und Wartungsarbeiten erforderlich. (T4 zur Rettung!)
Bearbeiten : Um die Verwirrung um das, wovon ich spreche, zu beseitigen , betrachten Sie das folgende Beispiel, das IQueryable gestohlen wurde. Kann Ihren Hund töten, Ihre Frau stehlen, Ihren Lebenswillen töten usw.
In dem Szenario, in dem Sie so etwas aussetzen würden:
Mit der API, über die ich spreche, können Sie eine Methode bereitstellen, mit der Sie
GetCustomersInAlaskaWithChildren
(oder eine andere Kombination von Kriterien) flexibel ausdrücken können , und das Repo führt sie als IQueryable aus und gibt die Ergebnisse an Sie zurück. Wichtig ist jedoch, dass es in der Repository-Ebene ausgeführt wird, sodass die verzögerte Ausführung weiterhin genutzt wird. Sobald Sie die Ergebnisse zurückerhalten haben, können Sie nach Herzenslust noch LINQ-to-Objekte darauf erstellen.Ein weiterer Vorteil eines solchen Ansatzes ist, dass dieses Repository, da es sich um POCO-Klassen handelt, sich hinter einem Web- oder WCF-Dienst befinden kann. Es kann AJAX oder anderen Anrufern ausgesetzt sein, die sich mit LINQ nicht auskennen.
quelle
IQueryable
ohne Freilegung derIQueryable
selbst . Wie machen Sie das mit der eingebauten API?Es gibt wirklich nur eine legitime Antwort: Es hängt davon ab, wie das Repository verwendet werden soll.
In einem Extremfall ist Ihr Repository ein sehr dünner Wrapper um einen DBContext, sodass Sie eine datenbankgesteuerte App mit einem Testfurnier versehen können. Es gibt wirklich keine reale Erwartung, dass dies getrennt verwendet wird, ohne dass eine LINQ-freundliche Datenbank dahinter steckt, denn Ihre CRUD-App wird das nie brauchen. Dann nutzen Sie doch IQueryable. Ich würde wahrscheinlich IEnumarable vorziehen, da Sie die meisten Vorteile erhalten [sprich: verzögerte Ausführung] und es sich nicht so schmutzig anfühlt.
Wenn Sie nicht so extrem sind, würde ich mich bemühen, den Geist des Repository-Musters auszunutzen und geeignete materialisierte Sammlungen zurückzugeben, die keine Auswirkungen auf eine zugrunde liegende Datenbankverbindung haben.
quelle
IEnumerable
ist es wichtig zu beachten, dass dies((IEnumerable<T>)query).LastOrDefault()
sehr unterschiedlich istquery.LastOrDefault()
(vorausgesetzt, esquery
ist einIQueryable
). Es ist also nur sinnvoll, zurückzukehren,IEnumerable
wenn Sie trotzdem alle Elemente durchlaufen.NEIN, wenn Sie testgesteuerte Entwicklung / Unittesting durchführen möchten, da es keine einfache Möglichkeit gibt, ein Schein-Repository zu erstellen, um Ihre Geschäftslogik (= Repository-Consumer) isoliert von der Datenbank oder einem anderen IQueryable-Anbieter zu testen.
quelle
Aus rein architektonischer Sicht ist IQueryable eine undichte Abstraktion. Im Allgemeinen bietet es dem Benutzer zu viel Leistung. Davon abgesehen gibt es Orte, an denen es Sinn macht. Wenn Sie OData verwenden, ist es mit IQueryable sehr einfach, einen Endpunkt bereitzustellen, der leicht zu filtern, zu sortieren, zu gruppieren ist usw. Eigentlich ziehe ich es vor, DTOs zu erstellen, denen meine Entitäten zugeordnet sind, und IQueryable gibt stattdessen den DTO zurück. Auf diese Weise filtere ich meine Daten vor und verwende wirklich nur die IQueryable-Methode, um die Client-Anpassung der Ergebnisse zu ermöglichen.
quelle
Ich habe auch vor einer solchen Wahl gestanden.
Fassen wir also die positiven und negativen Seiten zusammen:
Positives:
Negative:
Ich würde gegen diesen Ansatz empfehlen. Erstellen Sie stattdessen mehrere Spezifikationen und implementieren Sie Repository-Methoden, mit denen die Datenbank abgefragt werden kann. Das Spezifikationsmuster ist eine hervorragende Möglichkeit, eine Reihe von Bedingungen zu kapseln.
quelle
Ich denke, dass es in den Anfangsphasen der Entwicklung durchaus akzeptabel ist, das IQueryable in Ihrem Repository verfügbar zu machen. Es gibt UI-Tools, die direkt mit IQueryable arbeiten und das Sortieren, Filtern, Gruppieren usw. übernehmen können.
Davon abgesehen denke ich, dass es unverantwortlich ist, nur das Rohöl im Endlager offenzulegen und es als Tag zu bezeichnen. Wenn Sie nützliche Operationen und Abfragehilfen für allgemeine Abfragen haben, können Sie die Logik für eine Abfrage zentralisieren und gleichzeitig den Konsumenten des Repository eine kleinere Schnittstellenoberfläche zur Verfügung stellen.
In den ersten Iterationen eines Projekts kann IQueryable schneller im Repository verfügbar gemacht werden. Vor der ersten vollständigen Version würde ich empfehlen, die Abfrageoperation privat oder geschützt zu machen und die wilden Abfragen unter einem Dach zusammenzufassen.
quelle
Was ich gesehen habe (und was ich selbst implementiere) ist Folgendes:
Auf diese Weise haben Sie die Flexibilität, eine
IQueryable.Where(a => a.SomeFilter)
Abfrage in Ihrem RepoconcreteRepo.GetAll(a => a.SomeFilter)
durchzuführen , ohne ein LINQy-Geschäft zu gefährden.quelle