Ich bin Pro-Repository, obwohl ich mich von generischen Repository-Mustern entfernt habe. Stattdessen richte ich meine Repositorys an der Geschäftsfunktion aus, die sie erfüllen. Die Repositorys zielen nicht darauf ab, das ORM zu abstrahieren, da ich nicht erwarte, dass dies geändert wird, und gleichzeitig vermeide ich, ein Repository zu detailliert zu gestalten. (Dh CRUD) Stattdessen dienen meine Repositories zwei bis drei Hauptzwecken:
- Datenabruf
- Datenerstellung
- Harte Löschung
Beim Abrufen von Daten wird das Repository immer zurückgegeben IQueryable<TEntity>
. Für die Datenerstellung wird TEntity zurückgegeben. Das Repository verwaltet meine Filterung auf Basisebene, z. B. den Status "Autorisierung" aktiv "für Systeme, die Soft-Delete-Muster verwenden, und den Status" aktuell "für Systeme, die historische Daten verwenden. Die Datenerstellung ist nur dafür verantwortlich, dass die erforderlichen Referenzen aufgelöst und verknüpft werden und dass die Entität eingerichtet und einsatzbereit ist.
Die Datenaktualisierung liegt in der Verantwortung der Geschäftslogik, die mit den betreffenden Entitäten zusammenarbeitet. Dies kann beispielsweise Validierungsregeln umfassen. Ich versuche nicht, das in eine Repository-Methode zu kapseln.
Das Löschen in den meisten meiner Systeme erfolgt durch sanftes Löschen, sodass es unter die Datenaktualisierung fällt. (IsActive = false) Bei Hard-Deletes wäre dies ein Einzeiler im Repository.
Warum Repositories? Testfähigkeit. Sicher, DbContext kann verspottet werden, aber es ist einfacher, eine zurückgegebene Klasse zu verspottenIQueryable<TEntity>
. Sie spielen auch gut mit dem UoW-Muster. Ich persönlich verwende das DbContextScope-Muster von Mehdime, um die Arbeitseinheit auf der gewünschten Ebene festzulegen (z. B. Controller in MVC) und meine Controller und Helfer-Serviceklassen die Repositorys verwenden zu lassen, ohne Verweise darauf übergeben zu müssen der UoW / dbContext herum. Die Verwendung von IQueryable bedeutet, dass Sie nicht viele Wrapper-Methoden im Repository benötigen und Ihr Code die Verwendung der Daten optimieren kann. Beispielsweise muss das Repository keine Methoden wie "Exists" oder "Count" verfügbar machen oder versuchen, Entitäten mit anderen POCOs zu verpacken, wenn Sie Teilmengen von Daten wünschen. Sie müssen nicht einmal eifrige Ladeoptionen für verwandte Daten verarbeiten, die Sie möglicherweise benötigen oder nicht. Durch Übergeben von IQueryable kann der aufrufende Code:
.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()
Sehr flexibel und von einem Test-PoV aus muss mein verspottetes Repository lediglich Listen mit aufgefüllten Entitätsobjekten zurückgeben.
Bei generischen Repositorys habe ich mich größtenteils von diesen entfernt, da Ihre Controller / Services, wenn Sie ein Repository pro Tabelle haben, Verweise auf mehrere Repositorys enthalten, um eine Geschäftsaktion auszuführen. In den meisten Fällen führen nur ein oder zwei dieser Repositorys tatsächlich Schreibvorgänge aus (vorausgesetzt, Sie verwenden die Navigationseigenschaften ordnungsgemäß), während der Rest diejenigen mit Lesevorgängen unterstützt. Ich hätte lieber so etwas wie ein OrdersRepository, das in der Lage ist, Bestellungen zu lesen und zu erstellen und relevante Lookups usw. (leichte Kundenobjekte, Produkte usw.) als Referenz beim Erstellen einer Bestellung zu lesen, als 5 oder 6 verschiedene Repositorys zu treffen. Es kann gegen DNRY-Puristen verstoßen, aber mein Argument dafür ist, dass das Repository dazu dient, Aufträge zu erstellen, die die zugehörigen Referenzen enthalten.Repository<Product>
Um Produkte zu erhalten, bei denen ich für eine Auftragsbasis nur eine Entität mit einer Handvoll Feldern benötige. In meinem OrderRepository wird möglicherweise eine .GetProducts()
Methode zurückgegeben, IQueryable<ProductSummary>
die meiner Meinung nach besser ist als eine Repository<Product>
, die mehrere "Get" -Methoden enthält, um zu versuchen, verschiedene Bereiche der Anwendungsanforderungen zu bedienen, und / oder einen komplexen Pass-In-Filterausdruck.
Ich entscheide mich für einfacheren Code, der einfach zu befolgen, zu testen und abzustimmen ist. Es kann auf schlechte Weise missbraucht werden, aber ich hätte lieber etwas, bei dem Missbrauch leicht zu erkennen und zu korrigieren ist, als zu versuchen, den Code auf eine Weise zu "sperren", die nicht missbraucht werden kann, daran zu scheitern und dann etwas zu haben, das ist Ein Albtraum, um tatsächlich das zu tun, was der Kunde bezahlt, um es am Ende tun zu lassen. :) :)