90 Stimmen

Wie verwendet man das Repository-Muster richtig?

Ich frage mich, wie ich meine Repositories gruppieren sollte? Wie aus den Beispielen, die ich auf der asp.net mvc gesehen und in meinen Büchern verwenden sie im Grunde ein Repository pro Datenbanktabelle. Aber das scheint wie eine Menge von Repositories führen Sie zu haben, rufen Sie viele Repositories später für Mocking und Zeug.

Ich denke also, ich sollte sie gruppieren. Ich bin mir jedoch nicht sicher, wie ich sie gruppieren soll.

Momentan habe ich ein Registrierungs-Repository erstellt, um alle meine Registrierungsdaten zu verwalten. Allerdings gibt es etwa 4 Tabellen, die ich aktualisieren muss, und bevor ich 3 Repositories hatte, um dies zu tun.

Eine der Tabellen ist zum Beispiel eine Lizenztabelle. Wenn sie sich registrieren, schaue ich mir ihren Schlüssel an und prüfe, ob er in der Datenbank vorhanden ist. Was passiert nun, wenn ich diesen Lizenzschlüssel oder etwas anderes aus dieser Tabelle an einem anderen Ort als der Registrierung überprüfen muss?

Ein Punkt könnte die Anmeldung sein (prüfen, ob der Schlüssel nicht abgelaufen ist).

Was würde ich also in dieser Situation tun? Den Code noch einmal neu schreiben (DRY brechen)? Versuchen, diese 2 Repositories zusammenzuführen und hoffen, dass keine der Methoden zu einem anderen Zeitpunkt benötigt wird (z. B. könnte ich eine Methode haben, die prüft, ob userName verwendet wird - vielleicht brauche ich das an anderer Stelle).

Auch wenn ich sie zusammen verschmelzen würde ich entweder 2 Service-Schichten, die auf das gleiche Repository gehen, da ich denke, mit der ganzen Logik für 2 verschiedene Teile einer Website wäre lang und ich müsste Namen wie ValidateLogin(), ValdiateRegistrationForm(), ValdiateLoginRetrievePassword() und etc. haben.

Oder das Repository trotzdem anrufen und einfach einen seltsam klingenden Namen verwenden?

Es scheint schwer zu sein, ein Repository zu erstellen, dessen Name allgemein genug ist, so dass man es für viele Bereiche der Anwendung verwenden kann und es trotzdem Sinn macht, und ich glaube nicht, dass der Aufruf eines anderen Repositorys in einem Repository eine gute Praxis wäre.

41voto

Arnis Lapsa Punkte 42566

Eine Sache, die ich falsch gemacht habe, als ich mit dem Repository-Muster herumgespielt habe - genau wie Sie, dachte ich, dass sich die Tabelle 1:1 auf das Repository bezieht. Wenn wir einige Regeln aus dem Domain Driven Design anwenden, verschwindet das Problem der Gruppierung von Repositories oft.

Das Repository sollte per Aggregat Wurzel und nicht Tabelle. Das bedeutet - wenn ein Wesen nicht allein leben sollte (d.h. - wenn Sie einen Registrant die sich an bestimmten Registration ) - es ist nur eine Entität, sie braucht kein Repository, sie sollte über das Repository des Aggregats Root, zu dem sie gehört, aktualisiert/erstellt/abgerufen werden.

Natürlich kann diese Technik zur Reduzierung der Anzahl der Repositories (eigentlich ist es eher eine Technik zur Strukturierung des Domänenmodells) in vielen Fällen nicht angewendet werden, da jede Entität eine aggregierte Wurzel sein soll (das hängt stark von Ihrer Domäne ab, ich kann nur blinde Vermutungen anstellen). In Ihrem Beispiel - License scheint eine aggregierte Wurzel zu sein, denn Sie müssen in der Lage sein, sie zu überprüfen, ohne den Kontext der Registration Einheit.

Aber das schränkt uns nicht auf Kaskaden-Repositorien ein ( Registration Repository darf referenzieren License Repository, falls erforderlich). Das schränkt uns nicht auf die Referenz License Repository (vorzugsweise - über IoC) direkt von Registration Objekt.

Versuchen Sie einfach, Ihr Design nicht durch technologiebedingte Komplikationen oder Missverständnisse zu verkomplizieren. Gruppierung von Repositories in ServiceX nur weil man nicht 2 Repositories aufbauen will, ist das keine gute Idee.

Viel besser wäre es, ihm einen richtigen Namen zu geben - RegistrationService d.h.

Aber Dienstleistungen sollten im Allgemeinen vermieden werden - sie sind oft die Ursache für anämisches Domänenmodell .

EDIT:
Beginnen Sie mit der Verwendung von IoC. Es erleichtert wirklich den Schmerz der Injektion von Abhängigkeiten.
Anstatt zu schreiben:

var registrationService = new RegistrationService(new RegistrationRepository(),  
      new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());

werden Sie in der Lage sein zu schreiben:

var registrationService = IoC.Resolve<IRegistrationService>();

P.s. Es wäre besser, die so genannten Gemeinsamer Dienstleistungsverzeichnis aber das ist nur ein Beispiel.

5voto

neouser99 Punkte 1767

Eine Sache, mit der ich begonnen habe, um dieses Problem zu lösen, ist die Entwicklung von Diensten, die N-Repositories umhüllen. Hoffentlich können Ihre DI- oder IoC-Frameworks dabei helfen, das einfacher zu machen.

public class ServiceImpl {
    public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { }
}

Ergibt das einen Sinn? Auch verstehe ich, dass das Sprechen von Dienstleistungen in dieser Weise kann oder kann nicht tatsächlich mit DDD Prinzipien entsprechen, ich tue es einfach, weil es zu funktionieren scheint.

2voto

Michael Mann Punkte 757

Was ich tue, ist ich eine abstrakte Basisklasse wie folgt definiert haben:

public abstract class ReadOnlyRepository<T,V>
{
     V Find(T lookupKey);
}

public abstract class InsertRepository<T>
{
     void Add(T entityToSave);
}

public abstract class UpdateRepository<T,V>
{
     V Update(T entityToUpdate);
}

public abstract class DeleteRepository<T>
{
     void Delete(T entityToDelete);
}

Sie können dann Ihr Repository von der abstrakten Basisklasse ableiten und Ihr einzelnes Repository erweitern, solange sich z.B. die generischen Argumente unterscheiden;

public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>,
                                     ReadOnlyRepository<string, IRegistrationItem> 

etc....

Ich brauche die getrennten Repositories, weil wir für einige unserer Repositories Einschränkungen haben und dies uns maximale Flexibilität bietet. Ich hoffe, das hilft.

2voto

griegs Punkte 22284

Ich habe dies als mein Repository-Klasse und ja, ich erweitern in der Tabelle / Bereich Repository, aber immer noch habe ich manchmal zu brechen DRY.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MvcRepository
{
    public class Repository<T> : IRepository<T> where T : class
    {
        protected System.Data.Linq.DataContext _dataContextFactory;

        public IQueryable<T> All()
        {
            return GetTable.AsQueryable();
        }

        public IQueryable<T> FindAll(Func<T, bool> exp)
        {
            return GetTable.Where<T>(exp).AsQueryable();
        }

        public T Single(Func<T, bool> exp)
        {
            return GetTable.Single(exp);
        }

        public virtual void MarkForDeletion(T entity)
        {
            _dataContextFactory.GetTable<T>().DeleteOnSubmit(entity);
        }

        public virtual T CreateInstance()
        {
            T entity = Activator.CreateInstance<T>();
            GetTable.InsertOnSubmit(entity);
            return entity;
        }

        public void SaveAll()
        {
            _dataContextFactory.SubmitChanges();
        }

        public Repository(System.Data.Linq.DataContext dataContextFactory)
        {
            _dataContextFactory = dataContextFactory;
        }

        public System.Data.Linq.Table<T> GetTable
        {
            get { return _dataContextFactory.GetTable<T>(); }
        }

    }
}

エディテージ

public class AdminRepository<T> : Repository<T> where T: class
{
    static AdminDataContext dc = new AdminDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["MY_ConnectionString"].ConnectionString);

    public AdminRepository()
        : base( dc )
    {
    }

Ich habe auch einen Datentext, der mit Linq2SQL.dbml Klasse erstellt wurde.

Jetzt habe ich also ein Standard-Repository, das Standardaufrufe wie All und Find implementiert, und in meinem AdminRepository habe ich spezifische Aufrufe.

Das beantwortet aber nicht die Frage nach der Trockenheit, denke ich.

1voto

James Jones Punkte 8419

Hier ein Beispiel einer generischen Repository-Implementierung mit FluentNHibernate. Es ist in der Lage, jede Klasse zu persistieren, für die Sie einen Mapper geschrieben haben. Es ist sogar in der Lage, Ihre Datenbank auf der Grundlage der Mapper-Klassen zu generieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X