Ich bin viele Wege gegangen und habe viele Implementierungen von Repositories für verschiedene Projekte erstellt und ... Ich habe das Handtuch hineingeworfen und es aufgegeben, hier ist der Grund dafür.
Codierung für die Ausnahme
Codieren Sie die 1% ige Chance, dass sich Ihre Datenbank von einer Technologie zur anderen ändert? Wenn Sie über den zukünftigen Zustand Ihres Unternehmens nachdenken und sagen, dass dies eine Möglichkeit ist, müssen a) sie viel Geld haben, um sich eine Migration auf eine andere DB-Technologie leisten zu können, oder b) Sie wählen zum Spaß eine DB-Technologie aus oder c ) Bei der ersten Technologie, für die Sie sich entschieden haben, ist ein schrecklicher Fehler aufgetreten.
Warum die reichhaltige LINQ-Syntax wegwerfen?
LINQ und EF wurden entwickelt, damit Sie ordentliche Dinge damit tun können, um Objektgraphen zu lesen und zu durchlaufen. Das Erstellen und Verwalten eines Repositorys, das Ihnen die gleiche Flexibilität bietet, ist eine ungeheure Aufgabe. Nach meiner Erfahrung ist jedes Mal, wenn ich ein Repository erstellt habe, IMMER eine Geschäftslogik in die Repository-Schicht gelangt, um Abfragen entweder leistungsfähiger zu machen und / oder die Anzahl der Treffer in der Datenbank zu verringern.
Ich möchte nicht für jede einzelne Permutation einer Abfrage, die ich schreiben muss, eine Methode erstellen. Ich könnte genauso gut gespeicherte Prozeduren schreiben. Ich will nicht GetOrder
, GetOrderWithOrderItem
, GetOrderWithOrderItemWithOrderActivity
, GetOrderByUserId
, und so weiter ... Ich möchte nur die Haupt-Einheit und Traverse erhalten und beinhalten die Objektgraphen , wie ich so wenden Sie sich bitte.
Die meisten Beispiele für Repositories sind Bullshit
Wenn Sie nicht etwas WIRKLICH Bare-Bones wie ein Blog oder etwas entwickeln, werden Ihre Abfragen niemals so einfach sein wie 90% der Beispiele, die Sie im Internet rund um das Repository-Muster finden. Ich kann das nicht genug betonen! Dies ist etwas, das man durch den Schlamm kriechen muss, um herauszufinden. Es wird immer eine Abfrage geben, die Ihr perfekt durchdachtes Repository / Ihre perfekt durchdachte Lösung zerstört, und erst dann, wenn Sie sich selbst erraten und die technische Verschuldung / Erosion beginnt.
Teste mich nicht, Bruder
Aber was ist mit Unit-Tests, wenn ich kein Repository habe? Wie werde ich verspotten? Einfach, tust du nicht. Betrachten wir es aus beiden Blickwinkeln:
Kein Repository - Sie können das DbContext
mit einem IDbContext
oder anderen Tricks verspotten, aber dann testen Sie LINQ to Objects und nicht LINQ to Entities, da die Abfrage zur Laufzeit ermittelt wird ... OK, das ist also nicht gut! Nun ist es an dem Integrationstest, dies abzudecken.
Mit Repository - Sie können jetzt Ihre Repositorys verspotten und die dazwischen liegenden Layer testen. Großartig, oder? Nun, nicht wirklich ... In den oben genannten Fällen, in denen Sie Logik in die Repository-Schicht lecken müssen, um Abfragen leistungsfähiger und / oder weniger Treffer für die Datenbank zu machen, wie können Ihre Komponententests dies abdecken? Es ist jetzt in der Repo-Ebene und Sie möchten nicht IQueryable<T>
richtig testen ? Seien wir auch ehrlich, Ihre Unit-Tests werden nicht die Abfragen abdecken, die eine 20-Zeilen- .Where()
Klausel und enthalten.Include()
's eine Reihe von Beziehungen und schlägt die Datenbank erneut, um all diese anderen Dinge zu tun, bla, bla, bla sowieso, weil die Abfrage zur Laufzeit generiert wird. Da Sie ein Repository erstellt haben, um die Persistenz der oberen Ebenen nicht zu kennen, können Sie bei der Änderung Ihrer Datenbanktechnologie leider nicht die gleichen Ergebnisse zur Laufzeit garantieren, zurück zu Integrationstests. Der ganze Sinn des Repositorys scheint also seltsam.
2 Cent
Wir verlieren bereits viel Funktionalität und Syntax, wenn wir EF über einfach gespeicherte Prozeduren (Masseneinfügungen, Massenlöschungen, CTEs usw.) verwenden, aber ich codiere auch in C #, damit ich keine Binärdatei eingeben muss. Wir verwenden EF, damit wir die Möglichkeit haben, verschiedene Anbieter zu verwenden und unter anderem auf eine gut verwandte Art und Weise mit Objektgraphen zu arbeiten. Bestimmte Abstraktionen sind nützlich und andere nicht.
Coding for the exception
: Die Verwendung von Repositorys soll nicht in der Lage sein, das Datenbankmodul zu wechseln. Es geht darum, das Geschäft von der Beharrlichkeit zu trennen.DbSet
ist das Repository undDbContext
die Arbeitseinheit . Warum ein Repository-Muster implementieren, wenn ORM dies bereits für uns erledigt? Wechseln Sie zum Testen einfach den Anbieter aufInMemory
. Und mach deine Tests! Es ist in MSDN gut dokumentiert.Das Repository-Muster ist eine Abstraktion . Ziel ist es, die Komplexität zu reduzieren und den Rest des Codes unwissend zu machen. Als Bonus können Sie Unit- Tests anstelle von Integrationstests schreiben .
Das Problem ist, dass viele Entwickler den Zweck des Musters nicht verstehen und Repositorys erstellen, die persistenzspezifische Informationen an den Aufrufer weitergeben (normalerweise durch Offenlegen
IQueryable<T>
). Auf diese Weise erhalten sie keinen Vorteil gegenüber der direkten Verwendung des OP / M.Update, um eine andere Antwort zu adressieren
Codierung für die Ausnahme
Bei der Verwendung von Repositorys geht es nicht darum, die Persistenztechnologie zu wechseln (dh die Datenbank zu ändern oder stattdessen einen Webservice usw. zu verwenden). Es geht darum, Geschäftslogik von Persistenz zu trennen, um Komplexität und Kopplung zu reduzieren.
Unit-Tests gegen Integrationstests
Sie schreiben keine Komponententests für Repositorys. Zeitraum.
Durch die Einführung von Repositorys (oder einer anderen Abstraktionsschicht zwischen Persistenz und Geschäft) können Sie jedoch Komponententests für die Geschäftslogik schreiben. Das heißt, Sie müssen sich keine Sorgen machen, dass Ihre Tests aufgrund einer falsch konfigurierten Datenbank fehlschlagen.
Wie für die Abfragen. Wenn Sie LINQ verwenden, müssen Sie auch sicherstellen, dass Ihre Abfragen funktionieren, genau wie Sie es mit Repositorys tun. und das geschieht mit Integrationstests.
Der Unterschied besteht darin, dass Sie, wenn Sie Ihr Unternehmen nicht mit LINQ-Anweisungen gemischt haben, zu 100% sicher sein können, dass Ihr Persistenzcode fehlschlägt und nicht etwas anderes.
Wenn Sie Ihre Tests analysieren, werden Sie auch feststellen, dass sie viel sauberer sind, wenn Sie keine gemischten Bedenken haben (z. B. LINQ + Geschäftslogik).
Repository-Beispiele
Die meisten Beispiele sind Bullshit. das ist sehr wahr. Wenn Sie jedoch ein Designmuster googeln, finden Sie viele beschissene Beispiele. Dies ist kein Grund, die Verwendung eines Musters zu vermeiden.
Das Erstellen einer korrekten Repository-Implementierung ist sehr einfach. In der Tat müssen Sie nur eine einzige Regel befolgen:
Fügen Sie der Repository-Klasse erst dann etwas hinzu, wenn Sie es benötigen
Viele Codierer sind faul und versuchen, ein generisches Repository zu erstellen und eine Basisklasse mit vielen Methoden zu verwenden, die sie möglicherweise benötigen. YAGNI. Sie schreiben die Repository-Klasse einmal und behalten sie so lange, wie die Anwendung lebt (kann Jahre sein). Warum es versauen, indem man faul ist? Halten Sie es sauber, ohne dass eine Basisklasse vererbt wird. Dies erleichtert das Lesen und Verwalten erheblich.
(Die obige Aussage ist eine Richtlinie und kein Gesetz. Eine Basisklasse kann sehr gut motiviert sein. Denken Sie einfach nach, bevor Sie sie hinzufügen, damit Sie sie aus den richtigen Gründen hinzufügen.)
Altes Zeug
Fazit:
Wenn es Ihnen nichts ausmacht, LINQ-Anweisungen in Ihrem Geschäftscode zu haben oder sich um Komponententests zu kümmern, sehe ich keinen Grund, Entity Framework nicht direkt zu verwenden.
Aktualisieren
Ich habe sowohl über das Repository-Muster als auch darüber gebloggt, was "Abstraktion" wirklich bedeutet: http://blog.gauffin.org/2013/01/repository-pattern-done-right/
Update 2
Nochmals: Eine Entität mit mehr als 20 Feldern ist falsch modelliert. Es ist eine GOTTES Einheit. Mach es kaputt.
Ich behaupte nicht, dass
IQueryable
das nicht zum Abfragen gemacht war. Ich sage, dass es für eine Abstraktionsschicht wie das Repository-Muster nicht richtig ist, da es undicht ist. Es gibt keinen 100% vollständigen LINQ To Sql-Anbieter (wie EF).Sie alle haben implementierungsspezifische Dinge wie das eifrige Laden oder das Ausführen von SQL "IN" -Anweisungen. Das Offenlegen
IQueryable
im Repository zwingt den Benutzer, all diese Dinge zu wissen. Somit ist der gesamte Versuch, die Datenquelle zu abstrahieren, ein völliger Fehlschlag. Sie erhöhen lediglich die Komplexität, ohne einen Vorteil gegenüber der direkten Verwendung des OP / M zu erzielen.Implementieren Sie das Repository-Muster entweder korrekt oder verwenden Sie es überhaupt nicht.
(Wenn Sie wirklich mit großen Entitäten umgehen möchten, können Sie das Repository-Muster mit dem Spezifikationsmuster kombinieren . Dadurch erhalten Sie eine vollständige Abstraktion, die auch testbar ist.)
quelle
IMO haben sowohl die
Repository
Abstraktion als auch dieUnitOfWork
Abstraktion einen sehr wertvollen Platz in jeder sinnvollen Entwicklung. Die Leute werden über Implementierungsdetails streiten, aber genauso wie es viele Möglichkeiten gibt, eine Katze zu häuten, gibt es viele Möglichkeiten, eine Abstraktion zu implementieren.Ihre Frage ist speziell zu verwenden oder nicht zu verwenden und warum.
Wie Sie zweifellos festgestellt haben, haben Sie beide Muster bereits in Entity Framework integriert,
DbContext
ist dasUnitOfWork
undDbSet
ist dasRepository
. Sie müssen dasUnitOfWork
oder sichRepository
selbst im Allgemeinen nicht einem Unit-Test unterziehen , da dies lediglich die Erleichterung zwischen Ihren Klassen und den zugrunde liegenden Datenzugriffsimplementierungen erleichtert. Was Sie immer wieder tun müssen, ist, diese beiden Abstraktionen zu verspotten, wenn Sie die Logik Ihrer Dienste testen.Sie können mit externen Bibliotheken verspotten, fälschen oder was auch immer, indem Sie Ebenen von Codeabhängigkeiten (die Sie nicht steuern) zwischen der Logik, die den Test durchführt, und der zu testenden Logik hinzufügen .
Ein kleiner Punkt ist also, dass Sie eine eigene Abstraktion haben
UnitOfWork
undRepository
maximale Kontrolle und Flexibilität erhalten, wenn Sie Ihre Unit-Tests verspotten.Alles sehr gut, aber für mich ist die wahre Kraft dieser Abstraktionen, dass sie eine einfache Möglichkeit bieten, aspektorientierte Programmiertechniken anzuwenden und die SOLID-Prinzipien einzuhalten .
So haben Sie Ihre
IRepository
:Und seine Umsetzung:
Nichts Außergewöhnliches so weit , aber jetzt wollen wir einige Protokollierung hinzufügen - einfach , mit einem Logging - Ausstatter .
Alles erledigt und ohne Änderung an unserem bestehenden Code . Es gibt zahlreiche andere Querschnittsthemen, die wir hinzufügen können, wie z. B. Ausnahmebehandlung, Daten-Caching, Datenvalidierung oder was auch immer, und während unseres gesamten Entwurfs- und Erstellungsprozesses das Wertvollste, was wir haben, um einfache Funktionen hinzuzufügen, ohne unseren vorhandenen Code zu ändern ist unsere
IRepository
Abstraktion .Jetzt habe ich diese Frage in StackOverflow oft gesehen: "Wie lässt sich Entity Framework in einer Umgebung mit mehreren Mandanten arbeiten?".
https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant
Wenn Sie eine
Repository
Abstraktion haben, lautet die Antwort: "Es ist einfach, einen Dekorateur hinzuzufügen."IMO sollten Sie immer eine einfache Abstraktion über Komponenten von Drittanbietern platzieren, auf die an mehr als einer Handvoll Stellen verwiesen wird. Aus dieser Perspektive ist ein ORM der perfekte Kandidat, da in so vielen Teilen unseres Codes darauf verwiesen wird.
Die Antwort, die normalerweise in den Sinn kommt, wenn jemand sagt: "Warum sollte ich eine Abstraktion (z. B.
Repository
) über diese oder jene Bibliothek von Drittanbietern haben?" Lautet: "Warum sollten Sie nicht?"PS-Dekorateure lassen sich sehr einfach mit einem IoC-Container wie SimpleInjector anwenden .
quelle
Erstens ist EF selbst, wie aus einer Antwort hervorgeht, ein Repository-Muster. Es ist nicht erforderlich, eine weitere Abstraktion zu erstellen, nur um es als Repository zu bezeichnen.
Mockable Repository für Unit Tests, brauchen wir es wirklich?
Wir lassen EF in Unit-Tests kommunizieren, um die DB zu testen, um unsere Geschäftslogik direkt mit der SQL-Test-DB zu testen. Ich sehe keinen Vorteil darin, ein Repository-Muster überhaupt zu verspotten. Was ist wirklich falsch an Unit-Tests gegen Testdatenbanken? Da es sich um Massenoperationen handelt, sind diese nicht möglich und wir schreiben am Ende Roh-SQL. SQLite im Speicher ist der perfekte Kandidat für Unit-Tests mit realen Datenbanken.
Unnötige Abstraktion
Möchten Sie ein Repository erstellen, damit Sie EF in Zukunft problemlos durch NHbibernate usw. oder etwas anderes ersetzen können? Klingt nach einem guten Plan, ist er aber wirklich kostengünstig?
Linq tötet Unit-Tests?
Ich würde gerne Beispiele sehen, wie es töten kann.
Abhängigkeitsinjektion, IoC
Wow, das sind großartige Worte, sicher, dass sie theoretisch großartig aussehen, aber manchmal muss man sich zwischen großartigem Design und großartiger Lösung entscheiden. Wir haben das alles genutzt und am Ende alles in den Müll geworfen und einen anderen Ansatz gewählt. Größe gegen Geschwindigkeit (Größe des Codes und Geschwindigkeit der Entwicklung) sind im wirklichen Leben von großer Bedeutung. Benutzer benötigen Flexibilität, es ist ihnen egal, ob Ihr Code in Bezug auf DI oder IoC großartig im Design ist.
Es sei denn, Sie erstellen Visual Studio
All diese großartigen Designs werden benötigt, wenn Sie ein komplexes Programm wie Visual Studio oder Eclipse erstellen, das von vielen Menschen entwickelt wird und in hohem Maße anpassbar sein muss. Alle großartigen Entwicklungsmuster kamen nach Jahren der Entwicklung dieser IDEs ins Spiel, und sie haben sich an einem Ort entwickelt, an dem all diese großartigen Designmuster so wichtig sind. Wenn Sie jedoch eine einfache webbasierte Gehaltsabrechnung oder eine einfache Geschäftsanwendung durchführen, ist es besser, dass Sie Ihre Entwicklung im Laufe der Zeit weiterentwickeln, anstatt Zeit damit zu verbringen, sie für Millionen Benutzer zu erstellen, wo sie nur für Hunderte von Benutzern bereitgestellt wird.
Repository als gefilterte Ansicht - ISecureRepository
Auf der anderen Seite sollte das Repository eine gefilterte Ansicht von EF sein, die den Zugriff auf Daten schützt, indem der erforderliche Füllstoff basierend auf dem aktuellen Benutzer / der aktuellen Rolle angewendet wird.
Dies erschwert das Repository jedoch noch mehr, da es in einer riesigen Codebasis zu warten ist. Am Ende erstellen die Benutzer unterschiedliche Repositorys für unterschiedliche Benutzertypen oder eine Kombination von Entitätstypen. Nicht nur das, wir haben auch viele DTOs.
Die folgende Antwort ist eine Beispielimplementierung von Filtered Repository, ohne eine ganze Reihe von Klassen und Methoden zu erstellen. Es kann sein, dass die Frage nicht direkt beantwortet wird, aber es kann nützlich sein, eine abzuleiten.
Haftungsausschluss: Ich bin Autor des Entity REST SDK.
http://entityrestsdk.codeplex.com
Vor diesem Hintergrund haben wir ein SDK entwickelt, das auf der Grundlage von SecurityContext ein Repository für gefilterte Ansichten erstellt, das Filter für CRUD-Operationen enthält. Und nur zwei Arten von Regeln vereinfachen komplexe Operationen. Der erste ist der Zugriff auf die Entität und der andere ist die Lese- / Schreibregel für die Eigenschaft.
Der Vorteil ist, dass Sie keine Geschäftslogik oder Repositorys für verschiedene Benutzertypen neu schreiben, sondern sie einfach blockieren oder ihnen den Zugriff gewähren.
Diese LINQ-Regeln werden für jede Operation anhand der Datenbank in der SaveChanges-Methode ausgewertet, und diese Regeln fungieren als Firewall vor der Datenbank.
quelle
Es gibt viele Debatten darüber, welche Methode korrekt ist, daher betrachte ich sie als akzeptabel, sodass ich immer die verwende, die mir am besten gefällt (Welches ist kein Repository, UoW).
In EF wird UoW über DbContext implementiert und die DbSets sind Repositorys.
Was die Arbeit mit der Datenschicht betrifft, arbeite ich direkt am DbContext-Objekt. Bei komplexen Abfragen werde ich Erweiterungsmethoden für die Abfrage erstellen, die wiederverwendet werden können.
Ich glaube, Ayende hat auch einige Posts darüber, wie schlecht es ist, CUD-Operationen zu abstrahieren.
Ich mache immer eine Schnittstelle und lasse meinen Kontext davon erben, damit ich einen IoC-Container für DI verwenden kann.
quelle
Was für EF am häufigsten gilt, ist kein Repository-Muster. Es ist ein Fassadenmuster (Abstraktion der Aufrufe von EF-Methoden in einfachere, benutzerfreundlichere Versionen).
EF ist derjenige, der das Repository-Muster (und auch das Muster der Arbeitseinheit) anwendet. Das heißt, EF ist derjenige, der die Datenzugriffsschicht abstrahiert, sodass der Benutzer keine Ahnung hat, dass es sich um SQLServer handelt.
Und dabei sind die meisten "Repositories" über EF nicht einmal gute Fassaden, da sie lediglich einzelne Methoden in EF auf einfache Weise abbilden, selbst wenn sie dieselben Signaturen haben.
Die beiden Gründe für die Anwendung dieses sogenannten "Repository" -Musters auf EF bestehen darin, das Testen zu vereinfachen und eine Teilmenge von "vordefinierten" Aufrufen darauf zu erstellen. An sich nicht schlecht, aber eindeutig kein Repository.
quelle
Linq ist heutzutage ein "Repository".
ISession + Linq ist bereits das Repository, und Sie benötigen weder
GetXByY
Methoden nochQueryData(Query q)
Verallgemeinerungen. Da ich ein wenig paranoid gegenüber der DAL-Nutzung bin, bevorzuge ich immer noch die Repository-Schnittstelle. (Unter dem Gesichtspunkt der Wartbarkeit müssen wir auch noch eine Fassade über bestimmte Datenzugriffsschnittstellen haben).Hier ist das Repository, das wir verwenden - es entkoppelt uns von der direkten Verwendung von nhibernate, bietet jedoch eine Linq-Schnittstelle (als ISession-Zugriff in Ausnahmefällen, die möglicherweise einem Refactor unterliegen).
quelle
Im Repository (oder wie auch immer man es nennt) geht es mir derzeit hauptsächlich darum, die Persistenzschicht zu abstrahieren.
Ich verwende es gekoppelt mit Abfrageobjekten, damit ich in meinen Anwendungen keine Kopplung an eine bestimmte Technologie habe. Und es erleichtert auch das Testen erheblich.
Also neige ich dazu zu haben
Fügen Sie möglicherweise asynchrone Methoden mit Rückrufen als Delegaten hinzu. Das Repo ist einfach generisch zu implementieren , sodass ich keine Zeile der Implementierung von App zu App berühren kann. Nun, das ist zumindest wahr, wenn ich NH benutze. Ich habe es auch mit EF gemacht, aber ich habe EF gehasst. 4. Das Gespräch ist der Beginn einer Transaktion. Sehr cool, wenn einige Klassen die Repository-Instanz gemeinsam nutzen. Für NH entspricht ein Repo in meiner Implementierung einer Sitzung, die bei der ersten Anforderung geöffnet wird.
Dann die Abfrageobjekte
Für die Konfiguration verwende ich in NH nur, um die ISession zu übergeben. In EF macht mehr oder weniger keinen Sinn.
Eine Beispielabfrage wäre .. (NH)
Um eine EF-Abfrage durchzuführen, müsste sich der Kontext in der Abstract-Basis befinden, nicht in der Sitzung. Aber natürlich wäre das ifc dasselbe.
Auf diese Weise sind die Abfragen selbst gekapselt und leicht testbar. Das Beste ist, dass mein Code nur auf Schnittstellen basiert. Alles ist sehr sauber. Domänen- (Geschäfts-) Objekte sind genau das, z. B. gibt es keine Vermischung von Verantwortlichkeiten wie bei Verwendung des aktiven Datensatzmusters, das kaum testbar ist und Datenzugriffscode (Abfragecode) im Domänenobjekt mischt, und dabei Bedenken zu mischen (Objekt, das abruft) selbst??). Es steht weiterhin jedem frei, POCOs für die Datenübertragung zu erstellen.
Alles in allem bietet dieser Ansatz viel Wiederverwendung und Einfachheit von Code, ohne dass etwas verloren geht, was ich mir vorstellen kann. Irgendwelche Ideen?
Und vielen Dank an Ayende für seine großartigen Beiträge und sein anhaltendes Engagement. Es sind seine Ideen hier (Abfrageobjekt), nicht meine.
quelle
Für mich ist es eine einfache Entscheidung mit relativ wenigen Faktoren. Die Faktoren sind:
Wenn meine App also nicht die Nummer 2 rechtfertigen kann, trennen Sie Domain- und Datenmodelle, dann kümmere ich mich normalerweise nicht um Nummer 5.
quelle