it-swarm.com.de

Welche Daten sollte ein Repository zurückgeben?

Ich habe ein einfaches Projekt, in dem der Controller den Dienst aufruft und der Dienst das Repository aufruft, um die erforderlichen Daten abzurufen.

Angenommen, wir haben diese Domänenmodelle:

// this model has a RepositoryA built for it
Class A {
   public string Name { get; set; }
   public int B_Id { get; set; }
}

// this model has a RepositoryB built for it
Class B {
   public string Age{ get; set; }
}

Und dieser DTO:

Class MyDto {
   public string Name { get; set; }
   public string Age{ get; set; }
}

Und zu wissen (korrigieren Sie mich, wenn ich falsch liege), dass ein Repository kein DTO zurückgeben sollte und dass die Abfrage in einem RepositoryA durchgeführt werden muss, in dem wir beide Modelle A und B auswählen müssen, um eine Rückgabe zu erstellen, die a enthält Mix von Eigenschaften (komplexe Anforderung mit mehreren Verknüpfungen für verschiedene Tabellen im realen Projekt), welches Modell sollte das Repository zurückgeben, wenn nicht das DTO? Wissen Sie, dass der DTO bereits bereit ist, den Immobilienmix zu erhalten?

7
Uentee

Und zu wissen (korrigieren Sie mich, wenn ich falsch liege), dass ein Repository kein DTO zurückgeben sollte

Theoretisch sollte jede Ebene (= Projekt in Ihrer Lösung) ihre eigenen DTO-Objekte haben. In diesem Sinne sollten Ihre Repositorys ein DTO zurückgeben, dies ist jedoch nicht dasselbe DTO wie das "Geschäftslogik-DTO".

In Wirklichkeit brauchen wir jedoch nicht so viel Trennung. Die Vorteile überwiegen nicht den Aufwand. In den meisten Fällen reicht eine einzelne Entity-DTO-Konvertierung aus, die in der Regel in der Business-Schicht (BLL) erfolgt, die eine Schicht über der Repository-Schicht (DAL) liegt.
In diesem Sinne sollten Ihre Repositorys kein DTO zurückgeben. Aber Ihre Businessklassen sollten.


und dass die Abfrage in einem RepositoryA durchgeführt werden muss, in dem beide Modelle A und B ausgewählt werden müssen, um eine Rückgabe zu erstellen, die eine Mischung von Eigenschaften enthält (komplexe Anforderung mit mehreren Verknüpfungen für verschiedene Tabellen im realen Projekt)

Hier geht es um Realismus. Die Theorie kann einfach nicht in die Praxis umgesetzt werden.

Theoretisch sind Repositorys als entitätstypspezifische Datenanbieter konzipiert. Mit anderen Worten, Sie erhalten A vom ARepository und B vom BRepository.

Beim Umgang mit externen Speicherressourcen (Datenbankserver) ist der zum Abrufen von Daten erforderliche Aufwand jedoch nicht vernachlässigbar . Dies schafft ein Problem für uns. Für jeden Entitätstyp, den wir abrufen möchten, benötigen wir einen separaten Aufruf über ein eigenes Repository.

Nebenbemerkung: Wenn Sie entweder nicht mit einer externen Datenressource arbeiten oder nicht versuchen, einzelne Abfragen auszuführen, die Objekte mehrerer Typen zurückgeben, die Fortsetzung Diese Antwort ist für Sie irrelevant, da Sie mit jedem Typ aus seinem eigenen Repository sehr zufrieden sind.

Sobald Sie mit mehr als einem Entitätstyp in einer Anforderung arbeiten, ist es kontraproduktiv, diese in separate Abrufabfragen zu unterteilen. Es erstellt zwar eine saubere Codestruktur, wirkt sich jedoch dramatisch auf die Leistung aus. Dies wird doppelt ungeheuerlich, wenn Sie feststellen, dass ein SQL-Datenbankserver speziell für den Beitritt optimiert ist (mithilfe von Indizes), der Code, der die Datenbank (das Repository) aufruft, jedoch nicht in der Lage ist, denselben anmutigen Ansatz zum Mischen von Daten zu implementieren.

Die Verwendung von Repositories ist hier zu einem Gegenmuster geworden. Die Hindernisse, auf die Sie stoßen, werden von uns verursacht, die sich für die Verwendung von Repositorys entscheiden . Und eine "Lösung", die ein größeres Hindernis schafft, als sie lösen soll, ist keine Lösung.


A Arbeitseinheit löst viele der Probleme, die Repositorys im Hinblick auf die Transaktionssicherheit verursachen (wobei alle Repositorys denselben Kontext verwenden.

Eine Arbeitseinheit hilft jedoch nicht bei der Entscheidung, wo eine komplexe Datenabfrage geschrieben werden soll (ARepository? BRepository? ...)


Dies ist eine schwierige Wahl. In gewisser Weise sind Repositorys wirklich nett, insbesondere in Kontexten vom Typ einer Entität (z. B. einfache CRUD-Funktionalität). Andererseits erschwert es das Abrufen komplexer Daten massiv.

Ich habe keine allgemein vereinbarte Lösung dafür gefunden. Ich habe jedoch an mehreren Projekten gearbeitet, bei denen eine (oder mehrere) Vereinbarungen getroffen wurden, um zumindest den Speicherort des Codes (insbesondere bei komplexen Datenabfragen) etwas sinnlich zu halten.

Es ist immer gut, eine Arbeitseinheit zu implementieren.

Dies ist nur eine Liste von Vereinbarungen, die ich bei mehreren Projekten getroffen habe, an denen ich gearbeitet habe. Ich kann nicht wirklich übereinander setzen

  1. Komplexe Datenabfragen werden in das Repository ihres Entitätstyps main gestellt.

    • Eine Abfrage, die eine Liste von Autos abruft und deren Besitzer enthält, gehört zum CarRepository.
    • Eine Abfrage, die eine Liste von Personen abruft und die Autos enthält, die sie besitzen, gehört zum PersonRepository.
    • Pro Sie können die alte Repository-Struktur beibehalten, aber zumindest weniger raten, wo der Code abgelegt werden soll.

    • Con Es gibt Randfälle, in denen es keinen eindeutigen "Haupt" -Entitätstyp gibt.

  2. Komplexe Datenabfragen werden in ein eigenes Repository gestellt. "Alte" Repositorys, die sich auf einen einzelnen Entitätstyp konzentrieren, sollten nur in CRUD-Operationen verwendet werden.
    • Wenn Sie eine Methode haben, die A und B Objekte speichert, sollten Sie ein ABRepository haben, das intern ARepository und BRepository verwendet (a Arbeitseinheit ist hier unglaublich wichtig!).
    • Pro Es trennt die CRUD-Logik von der Berichtslogik.
    • Con Wenn Sie viele Kombinationen haben (AB, AC, ABC, ACD, ...), die Liste der Repositorys wird aus Grenzen heraus wachsen.
    • Der Vorschlag, das "con" zu verhindern, besteht darin, diese Repositorys nach ihrer Funktion (YearlyReportsRepository) und nicht nur nach ihrer aggregierten Entitätsliste zu benennen Typen (PersonCarRepository).

welches Modell sollte das Repository zurückgeben, wenn nicht das DTO?

Wenn Sie Option 1 wählen, hat diese Frage eine einfache Antwort.

  • Eine Abfrage, die eine Liste von Autos abruft und deren Besitzer enthält, gehört zum CarRepository.

Mit anderen Worten, es gibt einen IEnumerable<Car> Zurück und folgt daher immer noch der Idee, dass "ein CarRepository Autos zurückgibt" (und möglicherweise einige verwandte Entitäten, aber sie sind nicht explizit Teil des Rückgabetyps).

  • Eine Abfrage, die eine Liste von Personen abruft und die Autos enthält, die sie besitzen, gehört zum PersonRepository.

Mit anderen Worten, es gibt einen IEnumerable<Person> Zurück und folgt daher immer noch der Idee, dass "ein PersonRepository Personen zurückgibt" (und möglicherweise einige verwandte Entitäten, aber sie sind nicht explizit Teil des Rückgabetyps).

Wenn Sie Option 2 wählen, argumentieren Sie implizit, dass ein komplexer Datenbericht etwas anderes ist als eine einfache CRUD-Operation. Dies bedeutet, dass Sie wahrscheinlich benutzerdefinierte Klassen (CarOwnerResult) vom benutzerdefinierten zurückgeben würden Repositorys (= nicht an einen einzelnen Entitätstyp gebunden).

Beachten Sie, dass bei einfachen Repositorys (die an einen einzelnen Entitätstyp gebunden sind) nach wie vor nur der gebundene Entitätstyp wie zuvor zurückgegeben werden kann.

6
Flater

Das Repository sollte ein Domänenmodell zurückgeben.

Sie sollten ein einziges Repository pro Datenbank haben und nicht eines pro Tabelle.

Wenn Sie in Ihrem Fall beide Domänenmodelle als einzelne Abfrage verwenden möchten, können Sie dies auch tun

  • Ändern Sie Ihr Domänenmodell so, dass A ein untergeordnetes Objekt B hat
  • Haben Sie noch zwei separate Methoden, aber speichern Sie die Ergebnisse im Cache, damit die zweite aufgerufene Methode die Abfrage nicht erneut ausführen muss.
  • Geben Sie ein Tuple<A,B> oder Dictionary<A,B>

Die Rückgabe eines Tupels ist meine am wenigsten bevorzugte Option. Wenn Sie damit fertig sind, sollten Sie wahrscheinlich stattdessen ein komplexes Objekt erstellen.

In einigen Fällen kann eine Dictionary-Rückgabe sinnvoll sein, aber auch hier ist es schwierig, eine Beziehung zu codieren, die Sie möglicherweise nicht immer möchten. Die Gefahr besteht darin, dass Sie am Ende eine Million Methoden erhalten: GetAwithB, GetBwithC, GetXwithQandR usw. usw.

2
Ewan