2010-11-24 8 views
15

Ich habe die folgende Implementierung und möchte ein Feedback darüber, ob NHibernate korrekt für Sitzungen und Transaktionen verwendet wird.Korrekte Verwendung des NHibernate-Unit-of-Work-Musters und Ninject

public interface IUnitOfWork : IDisposable 
{ 
    ISession CurrentSession { get; } 
    void Commit(); 
    void Rollback(); 
} 

public class UnitOfWork : IUnitOfWork 
{ 
    private readonly ISessionFactory _sessionFactory; 
    private readonly ITransaction _transaction; 

    public UnitOfWork(ISessionFactory sessionFactory) 
    { 
     _sessionFactory = sessionFactory; 
     CurrentSession = _sessionFactory.OpenSession(); 
     _transaction = CurrentSession.BeginTransaction(); 
    } 

    public ISession CurrentSession { get; private set; } 

    public void Dispose() 
    { 
     CurrentSession.Close(); 
     CurrentSession = null; 
    } 

    public void Commit() 
    { 
     _transaction.Commit(); 
    } 

    public void Rollback() 
    { 
     if (_transaction.IsActive) _transaction.Rollback(); 
    } 
} 

Ninject

Bindung
Bind<IUnitOfWork>().To<UnitOfWork>().InTransientScope(); 
Bind<ISessionFactory>().ToProvider<NHibernateSessionFactoryProvider>().InSingletonScope(); 
Bind<IRepository>().To<Repository>().InTransientScope(); 

Hier ist ein Beispiel für die Nutzung:

public class Repository : IRepository 
{ 
    private readonly ISessionFactory _sessionFactory; 

    public Repository(ISessionFactory sessionFactory) 
    { 
     _sessionFactory = sessionFactory; 
    } 

    public void Add(IObj obj) 
    { 
     using (var unitOfWork = new UnitOfWork(_sessionFactory)) 
     { 
      unitOfWork.CurrentSession.Save(obj); 
      unitOfWork.Commit(); 
     }   
    } 
} 

In meiner vorherigen Implementierung würde ich wie IUnitOfWork in mein Repository Konstruktor injiziert so

public Repository(IUnitOfWork unitOfWork) 
    {... 

Die Dispose() -Methode wurde jedoch nicht ausgeführt, was dazu führte, dass ein nachfolgender Aufruf diese Ausnahme auslöst: "Auf ein entsorgtes Objekt kann nicht zugegriffen werden. Objektname: 'AdoTransaction'. "

+3

Nicht sicher, was Ihre endgültige Entscheidung war ABER tun Sie es nicht! :) Im Ernst, NHibernate fügt bereits eine Menge Abstraktion und ein UoW-Muster hinzu. Verwenden Sie einfach NHibernate direkt aus der Box und halten Sie Ihren Code einfach. Willst du wirklich noch mehr Abstraktion zum Debuggen? –

Antwort

35

Erste Beobachtung: Ihr Repository sollte die Arbeitseinheit nicht festlegen. Dies vereitelt den ganzen Punkt der Arbeitseinheit. Indem Sie Ihre Änderungen sofort im Repository speichern, sind Sie" Micro-Management "der NHibernate Sitzung.

Die Arbeitseinheit sollte in der Anwendung/Service-Schicht höher auf den Stapel verwiesen werden. Dies ermöglicht Ihnen Anwendungscode, der mehrere Aktionen, möglicherweise auf verschiedenen Repositories und immer noch bei Das Ende commit alles auf einmal

Die UnitOfWork-Klasse selbst sieht gut aus, obwohl Sie sich fragen sollten, ob Sie es wirklich brauchen.In NHibernate, die ISessi auf IS ist Ihre Arbeitseinheit. Ihre UnitOfWork-Klasse scheint nicht viel Wert hinzuzufügen (vor allem, da Sie die CurrentSession-Eigenschaft ohnehin bereitstellen).

Aber Sie müssen darüber nachdenken, ist es Lebenszeit. Ich denke, Sie haben es in diesem Punkt falsch. Die Verwaltung der Sitzungslebensdauer hängt von der Art der Anwendung ab, die Sie entwickeln: In einer Webanwendung möchten Sie in der Regel eine Arbeitseinheit pro Anfrage haben (möglicherweise möchten Sie auf "Sitzung pro Anfrage anfordern" googeln). In einer Desktop-App ist es etwas komplizierter, Sie werden die meiste Zeit eine Sitzung pro Bildschirm oder eine Konversation pro Geschäftsvorgang wünschen.

+0

Danke für das Feedback Jeroenh. Unsere Anwendung muss nur einfache Crud-Operationen ausführen und muss nicht mehrere Aktionen ausführen und alles gleichzeitig übernehmen. Grund, warum ich nicht direkt gegen ISession arbeiten möchte, ist, weil ich wollte, dass UnitOfWork die Eröffnungssitzungen abstrahiert, eine Transition startet und eine Sitzung beendet. –

+6

Der Vorteil von IUnitOfWork ist die reduzierte Kopplung mit ORM. Oder zumindest ist das der einzige Vorteil, den ich mir vorstellen kann :) –

+5

@Ryan Barett, aber was nutzt das? http://ayende.com/Blog/archive/2010/07/30/the-false-myth-of-capsulating-data-access-in-the-dal.aspx – jeroenh

8

Ich habe eine meist CRUD-Art von Anwendung, und ich implementierte das Unit Of Work mit Repository-Muster, konnte aber nicht wirklich von der Sitzung/Transaktion trennen. Sitzungen und Transaktionen benötigen unterschiedliche Lebensdauern. In der Desktop-Welt ist eine Sitzung normalerweise "pro Bildschirm" und eine Transaktion ist "pro Benutzeraktion".

Weitere Informationen in dieser excellent article.

Also, was ich am Ende mit war:

  • IUnitOfWork -> Wraps Sitzung implementiert IDisposable
  • IAtomicUnitOfWork -> Transaktion Wraps, implementiert IDisposable
  • IRepository -> Bietet Holen, Speichern, Löschen und Abfragezugriff

Ich habe es so gemacht, dass Sie einebenötigenzum Erstellen einer IAtomicUnitOfWork und Sie benötigen eine IAtomicUnitOfWork, um eine IRepository zu erstellen, so dass ordnungsgemäße Transaktionsverwaltung erzwungen.Das ist wirklich alles, was ich durch die Implementierung eigener Schnittstellen gewonnen habe.

Wie Jeroenh sagte, Sie sind fast genauso gut zu verwenden ISession und ITransaction, aber am Ende fühlte ich ein wenig besser schreiben alle meinen Code gegen eine Schnittstelle, die ich definiert.

+0

Nette Idee, die IUoW zum IRepository schickt. Soweit die konzeptionelle Aufteilung: IUoW ist per definitionem eine Transaktion. Das Konzept einer Sitzung ist ein wenig anders die Abstraktion über sie verdient einen anderen Namen (ich würde es IDatabaseGenericSession nennen) .. –

+0

@Ryan Barrett: Ja, ich denke, ein besserer Name für die Sitzung Wrapper wäre eine Art von "Kontext ". Ich denke, dass ObjectContext bereits in .NET enthalten ist, oder? Vielleicht EntityContext? –

3

Ein wichtiger Teil der Antwort liegt in dem, was Sie Ihre Transaktionsgrößen sein möchten. Gerade jetzt (wie Jeroenh angegeben hat) ist die Transaktion per Methodenaufruf in Ihrem Repository. Dies ist sehr klein und wird wahrscheinlich nicht benötigt. Ich habe eine ASP.MVC-Anwendung erstellt und verwendet eine Transaktionsgröße, die alles aus einer einzigen HTTP-Anfrage enthält. Dies könnte mehrere Datenbank-Lesevorgänge/Aktualisierungen sein. Ich benutze dieselbe Arbeitseinheit und Ninject für IOC. Werfen Sie einen Blick, vielleicht etwas helfen mit Ihren Fragen:

http://bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/

http://bobcravens.com/2010/07/using-nhibernate-in-asp-net-mvc/

http://bobcravens.com/2010/09/the-repository-pattern-part-2/

http://bobcravens.com/2010/11/using-ninject-to-manage-critical-resources/

Hoffnung, das hilft.

Bob

+0

Danke für die Ressourcen Bob. Obwohl meine Anwendung keine MVC-App ist, sind die Ideen die gleichen. –

+0

Hallo Bob, leider hat dein Blog seine URL von blog.bobcravens.com auf bobcravens.com geändert. Daher ist vieles kaputt, zum Beispiel als Link zu Bildern. Ich hoffe, Sie könnten es beheben. –