2010-07-31 6 views
12

Ich möchte das IRepository-Muster (unterstützt durch NHibernate, wenn es darauf ankommt) in einem kleinen Projekt verwenden. Die Domäne ist eine einfache, damit ich mich auf das IRepository-Muster konzentrieren kann. Die Klasse der einzelnen Domänen ist Movie, mit Eigenschaften für Year, Genre und Title. Meine Absicht wäre, Filme zu "bekommen", deren Eigenschaften den oben genannten Kriterien entsprechen.Benutzt ich IRepository richtig?

Convention scheint eine allgemeine IRepository Schnittstelle, ähnlich wie die folgenden haben zu sein:

public interface IRepository<T> 
{ 
    T Get(int id); 
    T[] GetAll(); 
    void Add(T item); 
    void Update(T item); 
    void Delete(T item); 
} 

Mit einer Basisimplementierung:

public abstract class Repository<T> : IRepository<T> 
{ 
    public T Get(int id) { ... } 
    public T[] GetAll() { ... } 
    public void Add(T item) { ... } 
    public void Update(T item) { ... } 
    public void Delete(T item) { ... } 
} 

Dann eine domänenspezifische Schnittstelle haben:

public interface IMovieRepository 
{ 
    Movie[] GetByGenre(Genre genre); 
    Movie[] GetByYear(int year); 
    Movie[] GetByTitle(string title); 
} 

Mit einer Implementierung, die auch die Basis 01 erweitertKlasse:

public class MovieRepository : Repository<Movie>, IMovieRepository 
{ 
    public Movie[] GetByGenre(Genre genre) { ... } 
    public Movie[] GetByYear(int year) { ... } 
    public Movie[] GetByTitle(string title) { ... } 
} 

ich brauchen würde, um die Durchführung zu der Basisklasse sowie den Beton ein, mit NHibernate hinzuzufügen, aber ich würde gerne wissen, ob ich auf dem richtigen Weg bin mit diesem Setup.

Es scheint nur ein wenig Overhead für nur eine Domänenklasse zu sein, obwohl es weniger auffällig wäre, wenn mehrere Domänenklassen beteiligt wären. Im Moment versuche ich, es einfach zu halten, damit ich das Konzept festhalten kann.

Antwort

2

würde ich sagen, Sie in das Repository der Nähe sind, die ich in einer Produktionslösung in Transportunternehmen für die Ressourcenplanung verwenden (unter Verwendung von NHibernate als auch) - so für den Anfang Sie auf der rechten Seite Pfad meiner Meinung nach.Ich stimme dbones bei der Verwendung von IEnumerables/IList anstelle von Arrays zu - am Ende schreibst du .ToArray() mehrmals :-).

Ein paar Dinge, die Sie interessieren könnten:

Favor Zusammensetzung über Vererbung - anstelle von der abstrakten Repository erben - lassen Sie es nicht abstrakt sein und es in dem ‚Ctor injiziert und die Anrufe delegieren - das macht Ihr Design robuster in bestimmten Situationen (zB für ein Query-only-Repository etc.). Auf diese Weise haben Sie auch die Möglichkeit, das abstrakte Repository instantiierbar zu machen (ist das ein Wort?) und zu steuern, ob es über alle Repositories verteilt werden soll.

Im Anschluss an diesem Punkt - möchten Sie vielleicht die Basis-Repository ändern, generische Methoden zu haben, anstatt von der generischen Schnittstelle vererben:

public class Repository 
{ 
    public void Add<T>(T entity) 
    { 
     using(var session = GetSession()) 
     using(var tx = session.BeginTransaction()) 
     { 
      session.Save(entity) 
      //Transaction handling etc. 
     } 
    } 
    .... //repeat ad nasseum :-) 
} 

Sie könnten die spezifischen Repositories lassen wollen haben Zugriff auf die ISession - Dies verbessert erheblich, wie flexibel Sie Ihre Abfragen machen können und das Eager-/Faul-Holen steuern können und Sie den vollen Nutzen von NHibernate usw. erhalten

public class Repository 
{ 
    public IList<T> WrapQueryInSession<T>(Func<ISession,IList<T> query) 
    { 
     using(var session = GetSession()) 
     using(var tx = session.BeginTransaction()) 
     { 
      var items = query(session); 
      //Handle exceptions transacitons etc. 
      return items; 
     } 
    } 
} 

Verbrauch:

public class MovieRepository : IMovieRepository 
{ 
    private Repository _repository; 
    public MovieRepository(Repository repository) 
    { 
     _repository = repository; 
    } 
    public IList<Movie> GetByYear(int year) 
    { 
     Func<ISession, IList<Movie> query = session => 
     { 
      var query = session.CreateQuery("from Movie"); //or 
      var query = session.CreateCriteria("from Movie"); //or 
      var query = session.Linq<Movie>(); 
      //set criteria etc. 
      return query.List<Movie>(); //ToList<Movie>() if you're using Linq2NHibernate 
     }: 
     return _repository.WrapQueryInSession(query); 
    } 
} 

Sie können auch einen bool Rückgabewert auf Ihren Methoden setzen wollen, wenn etwas schief geht - und vielleicht einen aus IEnumerable für Fehler, den Sinn in dem anrufenden Code machen .

Aber alles in allem - das sind nur meine Leckerbissen, die ich im Laufe der Zeit hinzugefügt habe, um besser mit meiner Nutzung zu entsprechen - und sie sind völlig optional, nur zum Nachdenken anregen :-). Ich denke, Sie sind auf dem richtigen Weg - ich sehe keine größeren Probleme in Ihrem Code.

Hoffnung macht das Sinn :-)

+0

Ich mag Ihren Vorschlag für die Verwendung eines nicht-abstrakten 'Repository', um die übliche CRUD-artige Low-Level-Arbeit zu behandeln, die von den spezifischen Repos delegiert wurde. Es wirft das Problem auf, eine "ISession" sowohl in den generischen Repo als auch in den spezifischen Repo zu bekommen, der sie verwendet. –

+0

Die ISession sollte über eine private ISessionFactory im Repository abgerufen werden. Das spezifische Repository verwendet dann eine WrapxxxInSession-Methode des generischen Repositorys. – Goblin

+0

Ah ja, das hilft. Ich mag die Gesamtidee ... Ein weiterer Vorteil ist, dass das injizierte IRepository zum Testen verspottet werden kann. Es wird den Trick geben, Objekte in der richtigen Reihenfolge zu konstruieren und zu übergeben, aber wahrscheinlich kann ein IoC-Tool damit umgehen. –

6
  1. versuchen Sie nicht, eine array zurück zu geben. Verwenden Sie IEnumerable<T>, ICollection<T> oder IList<T>, dies wird lose Ihren Code weiter koppeln.

  2. Ihre IMovieRepository-Schnittstelle. Dieses Repository enthält die CRUD. deshalb machen

IMovieRepository : IRepository<Movie> {}

Dies wird nicht Ihre MovieRepository Klasse ändern, dass die Schnittstelle korrekt umsetzt. Dadurch können Sie Ihre Klassen entkoppeln, wenn Sie die Implementierung zu einem späteren Zeitpunkt ändern möchten.

schließlich. Das ist für eine der Methoden in Ordnung. Da Sie spezialisierte Funktionen haben, haben Sie sich auf das Repository spezialisiert.

Es gibt andere Möglichkeiten, die es Ihnen ermöglichen, 1 Repositry-Klasse zu verwenden und die erforderliche Abfrage zu übergeben. Dies wird als Spezifikationsmuster bezeichnet. Ich habe ein Projekt, das diese auf Codeplex mit dem Bericht verwendet verwendet, um den anderen Weg zu haben, eine Methode, um die Kriterien zu übergeben. Es gibt ein Open-Source-Projekt namens Sharp Architecture, von dem ich glaube, dass es verschlüsselt ist.

hoffe, das hilft

+0

Danke für die Zeiger auf die Spezifikation Muster-Implementierung und die Kriterien-basierte Methoden. Beides sind interessante Ideen, und ich werde sie näher betrachten, obwohl ich im Moment die Dinge einfach und geradlinig halte. –

0

Als Stoff zum Nachdenken, wenn Ihre ORM der Wahl eines LINQ-Provider hat (und NH hat) Sie den abfragbaren Repository versuchen können, die zu einer Sammlung sehr ähnlich ist:

public interface IRepository<T> : ICollection<T>, IQueryable<T> 

ich habe einige davon auf meiner Website geschrieben: Repository or DAO?: Repository es Ähnlichkeiten mit Ihrer Konstruktion hat (nur weil eine Sammlung CRUD auch unterstützt), I der Ansatz bedeutet, versucht, dass Sie Code haben kann, die nicht notwendigerweise im Umgang mit einem Repository, wie es gegen dieprogrammiert werden kann 210 oder IQueryable Schnittstelle ...