2009-09-30 21 views
85

Ich frage mich, wie sollte ich meine Repositories gruppieren? Wie von den Beispielen, die ich auf dem asp.net mvc und in meinen Büchern gesehen habe, benutzen sie grundsätzlich ein Ablagefach pro Datenbanktabelle. Aber das scheint eine Menge von Repositories zu sein, die dazu führen, dass man später viele Repositories aufrufen muss, um sich zu verspotten.Wie wird das Repository-Muster korrekt verwendet?

Also ich rate ich sollte sie gruppieren. Ich bin mir jedoch nicht sicher, wie ich sie gruppieren soll.

Im Moment habe ich ein Registrierungs-Repository, um alle meine Registrierungs-Zeug zu behandeln. Es gibt jedoch 4 Tabellen, die ich aktualisieren muss und bevor ich 3 Repositories dazu hatte.

Zum Beispiel ist eine der Tabellen eine Lizenztabelle. Wenn sie sich registrieren, schaue ich auf ihren Schlüssel und überprüfe, ob es in der Datenbank existiert. Was passiert jetzt, wenn ich diesen Lizenzschlüssel oder etwas anderes von dieser Tabelle an einer anderen Stelle als der Registrierung überprüfen muss?

Ein Punkt könnte Login (überprüfen Sie, ob der Schlüssel nicht abgelaufen ist).

Was also würde ich in dieser Situation tun? Schreiben Sie den Code erneut (DRY brechen)? Versuchen Sie, diese beiden Repositories zusammenzufassen und hoffen, dass keine der Methoden zu einem anderen Zeitpunkt benötigt wird (vielleicht habe ich eine Methode, die prüft, ob userName verwendet wird - vielleicht werde ich das woanders brauchen).

Auch wenn ich sie zusammenführe, würde ich entweder 2 Service-Schichten benötigen, die zum selben Repository gehen, da ich denke, dass die ganze Logik für 2 verschiedene Teile einer Site lang wäre und ich hätte Namen wie ValidateLogin() , ValdiateRegistrationForm(), ValdiateLoginRetrievePassword() und usw.

Oder rufen Sie das Repository anyway und haben nur einen seltsam klingenden Namen herum?

Es scheint nur schwer zu machen ein Repository, das einen allgemein genug Namen hat, so dass Sie es für viele Spots Ihrer Anwendung verwenden können und immer noch sinnvoll und ich denke nicht, ein anderes Repository in einem Repository aufrufen wäre eine gute Praxis ?

+9

+1. Große Frage. – griegs

+0

Danke, dass es mich schon seit einiger Zeit nervt. Da ich finde, dass die, die ich in dem Buch gesehen habe, zu einfach sind, zeigen sie dir nicht, was in diesen Situationen zu tun ist. – chobo2

+0

Ich mache im Grunde das gleiche wie Sie und ja, wenn ich eine linq2sql-Klasse habe, die in mehr als 1 Repository verwendet wird, und ich die Tabellenstruktur ändern muss, brechen ich DRY. Weniger als ideal. Ich plane es jetzt ein wenig besser, also brauche ich nicht mehr als einmal eine linq2sql-Klasse, was eine gute Trennung von Sorgen ist, aber ich sehe einen Tag voraus, an dem das ein echtes Problem für mich sein wird. – griegs

Antwort

39

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

Repository sollte per Aggregate root und nicht Tabelle sein. Es bedeutet - wenn Entität nicht alleine leben sollte (dh - wenn Sie eine Registrant haben, die an einer bestimmten Registration teilnimmt) - es ist nur eine Entität, sie benötigt kein Repository, sie sollte aktualisiert/erstellt/über das Repository von Aggregat abgerufen werden Wurzel gehört es.

Natürlich kann diese Technik der Reduzierung der Anzahl von Repositories (tatsächlich - es ist eher eine Technik, um Ihr Domänenmodell zu strukturieren) nicht angewendet werden, da jede Entität eine aggregierte Wurzel sein soll (das hängt stark davon ab auf Ihrer Domain kann ich nur Blindschätzungen liefern). In Ihrem Beispiel - License scheint eine aggregierte Wurzel zu sein, weil Sie in der Lage sein müssen, sie ohne Kontext von Registration Einheit zu überprüfen.

Aber das beschränkt uns nicht auf Kaskaden-Repositories (Registration Repository kann Referenz License Repository bei Bedarf). Dies beschränkt uns nicht auf die Bezugnahme auf License Repository (bevorzugt - durch IoC) direkt von Registration Objekt.

Versuchen Sie einfach, Ihr Design nicht durch Komplikationen zu steuern, die durch Technologien oder Missverständnisse entstehen. Das Gruppieren von Repositories in ServiceX, nur weil Sie nicht 2 Repositories erstellen möchten, ist keine gute Idee.

Viel besser wäre es zu geben, einen richtigen Namen - RegistrationService heißt

Aber Dienste sollten generell vermieden werden - sie sind oft Ursache, die zu anemic domain model führt.

BEARBEITEN:
Beginnen Sie, IoC zu verwenden. Es erleichtert wirklich den Schmerz, Abhängigkeiten zu injizieren.
Statt zu schreiben:

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

können Sie schreiben:

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

P. S. Wäre besser, so genannte common service locator zu verwenden, aber das ist nur ein Beispiel.

+16

Hahaha ... Ich gebe Ratschläge, Service Locator zu verwenden. Ich freue mich immer zu sehen, wie dumm ich in der Vergangenheit war. –

+3

Hahaha ... Ich gebe Ratschläge, Repository-Muster zu verwenden. Immer Freude ... –

+1

@Arnis Es hört sich so an, als ob sich deine Meinung geändert hat - ich bin interessiert, wie würdest du diese Frage jetzt anders beantworten? – ngm

5

Eine Sache, die ich damit angefangen habe, ist, tatsächlich Dienste zu entwickeln, die N Repositories umhüllen. Hoffentlich können Ihre DI- oder IoC-Frameworks dazu beitragen, dies zu vereinfachen.

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

Macht das Sinn? Außerdem verstehe ich, dass, wenn ich von Diensten in diesem Landgut spreche, die DDD-Prinzipien tatsächlich einhalten können oder nicht, ich es einfach tue, weil es zu funktionieren scheint.

+1

Nopethat macht nicht viel Sinn. Ich verwende zur Zeit keine DI oder IoC Frameworks, weil ich genug auf meinem Teller habe, wie es ist. – chobo2

+1

Wenn Sie Ihre Repos mit nur einem neuen() instanziieren können, könnten Sie dies versuchen ... public ServiceImpl(): dies (neues Repo1, neues Repo2 ...) {} als zusätzlicher Konstruktor im Service. – neouser99

+0

Abhängigkeitsinjektion? Ich mache das schon, bin aber immer noch nicht sicher, was dein Code ist und was er löst. – chobo2

2

Was ich tue, ich habe eine abstrakte Basisklasse wie folgt definiert:

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 leiten Sie von der abstrakten Basisklasse Repository und erweitern einzigen Repository so lange auf die allgemeinen Argumente unterscheiden sich für Beispiel;

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

etc ....

Ich brauche die getrennte Repositories, weil wir Einschränkungen tun haben einige unserer Repositories und das gibt uns ein Höchstmaß an Flexibilität. Hoffe das hilft.

+0

Sie versuchen also, ein generisches Repository zu erstellen, um damit alles zu tun? – chobo2

+0

Ja. So weit, ist es gut. –

+0

Also, was geht eigentlich sagen die Update-Methode.Wie Sie dieses V-Update mögen und es in einem T-entitytoUpdate übergeben, aber es gibt keinen Code, der es tatsächlich aktualisiert oder gibt es? – chobo2

2

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

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>(); } 
     } 

    } 
} 

EDIT

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 eine Datacontext, die Linq2SQL.dbml Klasse erstellt wurde.

So jetzt habe ich ein Standard-Repository Standard-Aufrufe wie All und Find implementieren und in meinem AdminRepository habe ich bestimmte Anrufe.

Beantwortet die Frage von DRY nicht, obwohl ich nicht denke.

+0

Was "Repository" für? und wie CreateInstance? Nicht sicher, was alles was du tust. – chobo2

+0

Es ist generisch wie alles. Grundsätzlich benötigen Sie ein spezielles Repository für Ihr (Gebiet). Schau dir die obige Bearbeitung für mein AdminRepository an. – griegs

0

Ich schlage vor, Sie auf Sharp Architecture zu betrachten. Sie schlagen vor, ein Repository pro Entität zu verwenden. Ich benutze es derzeit in meinem Projekt und bin sehr zufrieden mit den Ergebnissen.

+0

Ich würde hier ein +1 geben, aber er hat angegeben, dass DI oder IoC-Container keine Option sind (das sind nicht die einzigen Vorteile von Sharp Arch). Ich nehme an, es gibt einige zu viel vorhandenen Code, den er arbeitet. – neouser99

+0

Was wird als Einheit betrachtet? Ist das die gesamte Datenbank? oder ist das eine Datenbanktabelle? DI- oder IoC-Container sind zu dieser Zeit keine Option, da ich das einfach nicht zu den anderen 10 Dingen lernen möchte, die ich gleichzeitig lerne. Sie sind etwas, was ich in meine nächste Überarbeitung meiner Website oder mein nächstes Projekt sehen werde. Auch wenn ich nicht sicher bin, ob es dieser sein wird, ein kurzer Blick auf die Website und es scheint, dass Sie wollen, nhirbrate zu verwenden, und ich benutze, um zu dieser aktuellen Zeit zu sql. – chobo2

1

Here is an example einer generischen Repository-Implementierung mit FluentNHibernate. Es kann jede Klasse beibehalten, für die Sie einen Mapper geschrieben haben. Es ist sogar in der Lage, Ihre Datenbank basierend auf den Mapper-Klassen zu generieren.

1

Das Repository-Muster ist ein schlechtes Entwurfsmuster. Ich arbeite mit vielen alten .Net-Projekten und dieses Muster verursacht in der Regel "Distributed Transactions", "Partial Rollback" und "Connection Pool Exhausted" Fehler, die vermieden werden könnten. Das Problem ist, dass das Muster versucht, Verbindungen und Transaktionen intern zu behandeln, aber diese sollten auf der Controller-Ebene behandelt werden. Auch EntityFramework abstrahiert bereits eine Menge Logik. Ich würde vorschlagen, stattdessen das Service-Muster zu verwenden, um geteilten Code wiederzuverwenden.

Verwandte Themen