Wann soll das Repository-Muster verwendet werden?

20

Ich habe kürzlich gelesen, dass es nicht empfehlenswert ist, das Repository-Muster in Verbindung mit einem ORM zu verwenden. Nach meinem Verständnis liegt dies daran, dass die Abstraktion, die sie über die SQL-Datenbank bereitstellen, zu undicht ist, um im Muster enthalten zu sein.

Ich habe ein paar Fragen dazu:

  1. Was machen Sie, wenn Sie ORMs austauschen möchten? Sie würden ORM-spezifischen Code in Ihrer Anwendung haben, wenn Sie ihn nicht in einem Repository enthalten.

  2. Ist das Repository-Muster auch dann noch gültig, wenn Sie kein ORM verwenden und ADO.NET für den Datenzugriff und das Auffüllen von Objektdaten selbst verwenden?

  3. Wenn Sie ein ORM verwenden, aber nicht das Repository-Muster, wo speichern Sie häufig verwendete Abfragen? Wäre es sinnvoll, jede Abfrage als Klasse darzustellen und eine Art Abfragefactory zum Erstellen von Instanzen zu haben?

user1450877
quelle
1
1) Sie werden niemals in der Lage sein, ein ORM aufgrund seines unterschiedlichen Verhaltens auszutauschen, selbst wenn beide linq unterstützen, verhalten sie sich so unterschiedlich, dass Ihre Anwendung nicht mehr funktioniert. zB fauler Ladevorgang, Dirty Tracking, Ghost-Proxies usw. Es ist jedoch schön, den ORM für eine In-Mem-Implementierung zum Testen austauschen zu können.
Roger Johansson
Was es wert ist, ist meine Sicht
Eric King
Wo hast du gelesen, dass es eine schlechte Praxis ist?
Dave Hillier

Antworten:

3

1) Stimmt, aber wie oft schalten Sie einen ORM aus?
2) Ich würde es so sagen, weil ein ORM-Kontext eine Art Repository ist und eine Menge Arbeit verbirgt, die das Erstellen von Abfragen, das Abrufen von Daten, das Zuordnen usw. umfasst. Wenn Sie kein ORM verwenden, muss sich diese Logik immer noch irgendwo befinden. Ich weiß nicht, ob dies als Repository-Muster im engeren Sinne des Wortes in
Frage kommen würde. 3) Das Einkapseln von Abfragen ist etwas, das ich häufig sehe, aber normalerweise eher zu Test- / Stubbing-Zwecken. Ansonsten wäre ich vorsichtig, wenn Sie eine Abfrage über verschiedene Teile einer Anwendung hinweg wiederverwenden, da Sie die Gefahr haben, eine Abhängigkeit von etwas zu erzeugen, das sich n-mal ändern könnte (n ist die Anzahl der Stellen, an denen Sie die Abfrage verwenden).

Stefan Billiet
quelle
1
1) Sie stellen die falsche Frage. Egal wie oft, es muss nur einmal sein. Angesichts der Anzahl der verfügbaren ORM-Optionen gibt es möglicherweise eine, die besser ist als die, die Sie bereits verwenden. Die Frage lautet nun: Was passiert, wenn Sie dies tun? Repository gibt Ihnen eine schöne Abstraktion. Ich war dort und wünschte, ich hätte eine solche Abstraktion gemacht.
Devnull
3
@devnull Ich stimme nicht zu. Wenn es höchstens einmal passieren würde, würde ich es als akzeptables Risiko betrachten. Wenn Sie befürchten, die falsche Wahl zu treffen, probieren Sie mehr, bevor Sie sich auf eine festlegen. Theoretisch klingt eine solche Abstraktion gut, aber in der Praxis wird ein ziemlich großer Teil der API Ihres ORM wiederhergestellt, weil Sie möglicherweise eines Tages irgendwo eine andere auswählen. Für mich ist das unnötiger Aufwand, redundanter Code und mehr Code, den Sie selbst warten und garantieren müssen. Außerdem sollte das Wechseln eines ORM nicht die gesamte Anwendung betreffen. Lernen Sie, Domaingrenzen zu setzen.
Stefan Billiet
ORM-Änderung ist nicht der einzige Grund für Repository. Wenn Sie einen [verteilten] Cache in Ihre Anwendung einführen müssen, werden alle Änderungen im Repository vorgenommen und Ihre BL wird sich aufgrund von Änderungen der Datenzugriffsebene nicht ändern.
Valery
Wäre der verteilte Cache in den meisten Fällen nicht in den ORM integriert?
user1450877
Ich denke es kommt auf den ORM an. Sie können sich für ein leichteres ORM als NHibernate oder EF entscheiden.
Valery
2

1) Was tun Sie, wenn Sie ORMs ausschalten möchten? Ihre Anwendung enthält einen bestimmten ORM-Code, wenn Sie ihn nicht in einem Repository enthalten.

Ich war noch nicht in einer Position, in der das Unternehmen plötzlich beschlossen hat, die Datenzugriffstechnologie zu wechseln. In diesem Fall sind einige Arbeiten erforderlich. Ich tendiere dazu, Datenzugriffsoperationen über Schnittstellen zu abstrahieren. Repository ist eine Möglichkeit, dies zu lösen.

Ich hätte dann eine andere Assembly für die konkrete Implementierung meiner Datenzugriffsebene. Zum Beispiel könnte ich haben:

Company.Product.Dataund Company.Product.Data.EntityFrameworkBaugruppen. Die erste Assembly würde nur für Schnittstellen verwendet, während eine andere eine konkrete Implementierung der Datenzugriffslogik von Entity Framework wäre.

2) Ist das Repository-Muster noch gültig, wenn Sie keinen ORM verwenden und ADO.net für den Datenzugriff und das Auffüllen von Objektdaten selbst verwenden?

Ich denke, es liegt an Ihnen, zu entscheiden, welches Muster gültig ist oder nicht. Ich habe ein Repository-Muster in der Präsentationsebene verwendet. Eine Sache, an die man denken sollte, ist, dass die Leute gerne Verantwortung in die Repositories werfen. Bevor Sie es wissen, wird Ihre Repository-Klasse tanzen, singen und alle möglichen Dinge tun. Sie möchten dies vermeiden.

Ich habe eine Repository-Klasse gesehen, die zu Beginn die Zuständigkeiten GetAll, GetById, Update und Delete hatte, was in Ordnung ist. Zu dem Zeitpunkt, als das Projekt abgeschlossen war, verfügte dieselbe Klasse über Dutzende von Methoden (Verantwortlichkeiten), die niemals hätten da sein dürfen. Zum Beispiel GetByForename, GetBySurname, UpdateWithExclusions und alle möglichen verrückten Sachen.

Hier kommen Abfragen und Befehle ins Spiel.

3) Wenn Sie ein ORM verwenden, aber nicht das Repository-Muster, werden häufig verwendete Abfragen gespeichert. Wäre es sinnvoll, jede Abfrage als Klasse darzustellen und eine Art Abfragefactory zum Erstellen von Instanzen zu haben?

Ich halte es für eine sehr gute Idee, Abfragen und Befehle anstelle von Repositorys zu verwenden. Ich mache folgendes:

  • Schnittstelle für eine Abfrage definieren. Dies wird Ihnen beim Unit-Test helfen. Z.Bpublic interface IGetProductsByCategoryQuery { ... }

  • Definieren Sie eine konkrete Implementierung für eine Abfrage. Sie können diese über ein IoC-Framework Ihrer Wahl einspeisen. Z.Bpublic class GetProductsByCategoryQuery : IGetProductsByCategoryQuery

Anstatt das Repository mit Dutzenden von Verantwortlichkeiten zu verschmutzen, gruppiere ich meine Abfragen einfach in Namespaces. Beispielsweise kann eine Schnittstelle für die obige Abfrage in: Company.SolutionName.Products.Queriesund die Implementierung in lebenCompany.SolutionName.Products.Queries.Implementation

Wenn es um das Aktualisieren oder Entfernen von Daten geht, verwende ich das Befehlsmuster auf die gleiche Weise.

Einige mögen anderer Meinung sein und sagen, dass Sie Dutzende von Klassen und Namespaces haben werden, bevor das Projekt abgeschlossen ist. Ja du wirst. In meinen Augen ist es eine gute Sache, wenn Sie die Lösung in der IDE Ihrer Wahl durchsuchen und sofort sehen können, welche Art von Verantwortlichkeiten eine bestimmte Komponente hat. Wenn Sie sich stattdessen für die Verwendung eines Repository-Musters entschieden haben, müssen Sie in jeder Repository-Klasse nachsehen, um die Verantwortlichkeiten herauszufinden.

CodeART
quelle
Ich mag die Idee, Befehle anstelle von generischen Funktionen zu haben. Wo kann ich mehr darüber erfahren, wie ich sie im Kontext des Datenzugriffs implementieren kann?
Ankush981
1

HAFTUNGSAUSSCHLUSS: Was folgt, basiert auf meinem Verständnis und meiner kurzen Erfahrung mit diesem Muster (dh Repository). Ich könnte es falsch machen ... in der Tat bin ich mir ziemlich sicher, dass ich es falsch mache :). Während dies also ein Versuch zur Beantwortung ist, handelt es sich auch um eine verdeckte Frage.

Ich verwende das Repository-Muster, um die Datenzugriffsebene zu abstrahieren, die in den meisten Fällen ein ORM ist. Es handelt sich um eine generische Schnittstelle mit Methoden für die Klassen "Erstellen", "Lesen", "Aktualisieren", "Löschen" und "Implementieren" für LINQ to SQL und EF. Ich könnte eine Implementierung erstellen, die in XML-Dateien auf der Festplatte schreibt (nur ein wildes Beispiel dafür, was ich damit machen könnte). Ich implementiere Unit of Work nicht, da es von den ORMs unterstützt wird. Notfalls könnte ich es wohl umsetzen. Ich mag es so, weil es mir bisher eine schöne Trennung gegeben hat. Ich sage nicht, dass es keine besseren Alternativen gibt.

Um Ihre Fragen zu beantworten:

  1. Ob es Ihnen gefällt oder nicht, wird sich ändern. Und wenn Sie eine Reihe von Anwendungen geschrieben haben und es an der Zeit ist, diese zu warten, aber das Gefühl hat, dass es mühsam ist, mit dem aktuellen ORM zu arbeiten und ihn zu ändern, werden Sie sich dafür preisen, dass Sie eine solche Abstraktion vorgenommen haben. Verwenden Sie einfach alles, mit dem Sie sich wohl fühlen, sei es Repository oder ein anderes Muster / Konzept.
  2. Ja, solange Sie damit den Datenzugriff trennen. Wie bereits erwähnt, können Sie eine Implementierung vornehmen, bei der Daten in Einfachdateien geschrieben werden.
  3. Ich verwende das Repository, um meine häufig verwendeten Abfragen und Abfrageobjekte aufzubewahren, wenn ich Abfragen habe, die ich optimieren möchte (die nicht so viele sind).

Um Ihnen ein weiteres Beispiel zu geben: Die Mitarbeiter von Umbraco haben den DI-Container entfernt, nur für den Fall, dass sie zu etwas anderem wechseln möchten.

devnull
quelle
0

Wenn der ORM die Flexibilität von LINQ, eifriges Laden usw. bietet, würde ich ihn nicht hinter einer zusätzlichen Ebene verstecken.
Wenn es sich um Raw-SQL (Micro-ORM) handelt, sollte ohnehin "Methode pro Abfrage" verwendet werden, um die Wiederverwendbarkeit zu erreichen, sodass das Repository-Muster gut passt.

What do you do if you want to switch out ORMs? 
You would have ORM specific code in your application if you do not contain it in a repository.

Warum müssten Sie wechseln?
Es sollte das verwendet werden, das die meisten Funktionen bietet, die Sie benötigen. Es ist möglich, dass eine neue Version von ormX neue Funktionen bringt und sich als besser herausstellt als die aktuelle, aber ...
Wenn Sie sich dafür entscheiden, das Orm auszublenden, können Sie nur die Funktionen verwenden, die alle Kandidaten gemeinsam haben.
Sie können z. B. keine Dictionary<string, Entity>Eigenschaften in Ihren Entitäten verwenden, weil ormY diese nicht verarbeiten kann.

Vorausgesetzt, LINQ wird verwendet, wechselt die Mehrheit der orm-Switches lediglich die Bibliotheksreferenzen und ersetzt sie session.Query<Foo>()durch context.Foosoder ähnliches, bis sie kompiliert werden. Langweilige Aufgabe, benötigt aber weniger Zeit als das Codieren der Abstraktionsschicht.

Is the repository pattern still valid when not using an ORM and you are using ADO.NET for data access and populating object data yourself?

Ja. Code sollte wiederverwendbar sein und das bedeutet, SQL-Gebäude, Objektmaterialisierung usw. an einem Ort zu platzieren (separate Klasse). Sie können auch die Klasse "XRepository" aufrufen und eine Schnittstelle daraus extrahieren.

If you use an ORM but not the repository pattern where do you keep commonly used queries? 
Would it be wise to represent each query as a class and have some sort of query factory to create instances?

Unter der Annahme, dass LINQ verwendet wird, wäre ein Klassenwrapper meiner Meinung nach übertrieben. Eine schönere Möglichkeit wären Erweiterungsmethoden

public static IQueryable<T> Published<T>(this IQueryable<T> source) where T : Page
{
    // at some point someone is going to forget to check that status
    // so it makes sense to extract this thing
    return source.Where(x => x.Status == Status.Published && x.PublishTime <= DateTime.UtcNow);
}

Jeder Code, der an mehreren Stellen verwendet wird (oder das Potenzial hat) und kompliziert genug ist (fehleranfällig), sollte an einer zentralen Stelle extrahiert werden.

Mike Koder
quelle