2016-12-23 6 views
0

Ich entwickle Data Access Layer mit NHibernate. Es wird in meiner Business Logic Schicht verwendet. Meine Anwendung ist Sammlung von mehreren anderen Anwendungen (ASP.NET MVC, Windows-Dienste, Windows Forms, ASP.NET-Web-API), die alle dieselbe Business Logic Layer verwenden. Business Logic Layer greift auf die Datenzugriffsebene zu. Anwendungen greifen NICHT direkt auf die Datenzugriffsebene zu.NHibernate: In welchem ​​Umfang sollte ich Transaktion verwenden?

Ich bin mir bewusst, dass ich nicht von impliziten Transaktion abhängen sollte und alle Datenbankaufrufe (einschließlich READ-Aufrufe) in expliziter Transaktion enthalten sollte.

In meinem Verständnis muss die Transaktion kurzlebig sein. Wenn es lange lebt, kann es Probleme verursachen. Siehe 1 und 2.

Viele Ressourcen (auf StackOverflow und anderen Sites) schlagen vor, Transaktionscode in using Block einzuschließen. Dies macht wirklich Sinn, da es hilft, explizite Transaktionen für jeden Datenbankaufruf zu verwenden. Es hilft auch, die Transaktion kurzlebig zu machen. Problem bei diesem Ansatz ist, dass die Stapelverarbeitung auf using Block beschränkt ist. UnitOfWork ist nicht vollständig ausgelastet. Auf der anderen Seite schlagen viele Ressourcen (auf StackOverflow und anderen Seiten) vor, Transaktionen auf einer höheren Ebene wie web-request (Sitzung pro Anfrage) in der Webanwendung ODER "pro Windows Form" in WinForms usw. zu verwenden. Dies verbessert das Batching von Abfragen und Hilfe, die UnitOfWork Muster besser ausnutzt. Problem bei diesem Ansatz ist, dass Transaktionen langlebig sind.

Weg 1] Expose Transaktion zur Anwendung und lassen Sie es behandeln.

Option 1 - Anrufer kann dies auf Anforderungsebene verarbeiten. Nachteil dieses Ansatzes ist, dass Transaktionen langlebig sein können.

Option 2 - Wenn er dies nicht tut, ist der einzige Weg, es auf niedrigerer Ebene wie "pro Methode" oder "pro kleinen Block von Code" zu behandeln. Aber jetzt muss er sicherstellen, dass er die Transaktion beginnt und sie ordnungsgemäß festlegt/zurücksetzt. Dies verringert die Lesbarkeit, ist schwierig zu überprüfen und zu debuggen im Falle eines Problems. Wenn dies ohnehin auf einer niedrigeren Ebene sein muss, warum sollte dies nicht stattdessen in die Business Logic-Schicht aufgenommen werden?

Weg 2] Steuern Sie Transaktion in Business Logic Layer.

Dies macht alle Transaktionen kurzlebig. Aber dies hat keinen Vorteil aus der Dosierung oder UnitOfWork. BLL weiß möglicherweise nicht im Voraus, wie Datenbankaufrufe durch Aufrufen der Anwendung durchgeführt werden.

Was ist die empfohlene Position zu Begin und Commit Transaktion, die guten Vorteil der Dosierung und ehren UnitOfWork nehmen?

Edit 1: (Nach Antwort zu akzeptieren)

Ich habe gerade ausgezeichnete article auf UnitOfWork. Im Folgenden sind einige der Auszüge aus dem gleichen:

Verwenden Sie nicht die Sitzung pro Operation Antipattern: nicht öffnen und schließen Sie eine Sitzung für jeden einfachen Datenbankaufruf in einem einzigen Thread. Das Gleiche gilt für Datenbanktransaktionen. Datenbankaufrufe in einer Anwendung werden mithilfe einer geplanten Sequenz ausgeführt. sie sind in atomare Arbeitseinheiten gruppiert.Dies bedeutet auch, dass das automatische Festschreiben nach jeder einzelnen SQL-Anweisung in einer Anwendung nutzlos ist, da dieser Modus für die Ad-hoc-SQL-Konsolenarbeit vorgesehen ist.

Datenbanktransaktionen sind niemals optional. Die gesamte Kommunikation mit einer Datenbank muss innerhalb einer Transaktion stattfinden. Auto-Commit-Verhalten zum Lesen von Daten sollte vermieden werden, da es unwahrscheinlich ist, dass viele kleine Transaktionen besser abschneiden als eine klar definierte Arbeitseinheit. Letzteres ist auch wartbarer und erweiterbar.

Das häufigste Muster in einer Client/Server-Anwendung mit mehreren Benutzern ist Session-pro-Request. In diesem Modell wird eine Anforderung vom Client an den Server gesendet, auf dem die Persistenzschicht für den Ruhezustand ausgeführt wird. Eine neue Hibernate-Sitzung wird geöffnet und alle Datenbankoperationen werden in dieser Arbeitseinheit ausgeführt. Nach Abschluss der Arbeit und nachdem die Antwort für den Kunden vorbereitet wurde, wird die Sitzung geleert und geschlossen. Verwenden Sie eine einzelne Datenbanktransaktion, um die Clientanforderung zu verarbeiten, indem Sie sie beim Öffnen und Schließen der Sitzung starten und festschreiben. Die Beziehung zwischen den beiden ist eins zu eins und dieses Modell ist eine perfekte Ergänzung für viele Anwendungen.

Das Session-pro-Request-Muster ist nicht die einzige Möglichkeit, Arbeitseinheiten zu entwerfen. Viele Geschäftsprozesse erfordern eine ganze Reihe von Interaktionen mit dem Benutzer, die mit Datenbankzugriffen verschachtelt sind. In Web- und Unternehmensanwendungen ist es nicht akzeptabel, dass eine Datenbanktransaktion eine Benutzerinteraktion umfasst.

Datenbank oder System, Transaktionsgrenzen sind immer erforderlich. Keine Kommunikation mit der Datenbank kann außerhalb einer Datenbanktransaktion auftreten (dies scheint viele Entwickler zu verwirren, die an den automatischen Festschreibungsmodus gewöhnt sind). Verwenden Sie immer klare Transaktionsgrenzen, auch für schreibgeschützte Operationen. Abhängig von Ihrer Isolationsstufe und Ihren Datenbankfunktionen ist dies möglicherweise nicht erforderlich, aber es gibt keinen Nachteil, wenn Sie Transaktionen immer explizit abgrenzen. Sicherlich wird eine einzelne Datenbanktransaktion sogar für das Lesen von Daten besser als viele kleine Transaktionen sein.

+0

Wie beschreiben Sie Ihre Anwendungsfälle? Verwenden Sie etwas wie Befehle oder Handler? Diese können natürliche Transaktionsgrenzen bilden. Darüber hinaus können Sie sie verzieren, um das Rauschen zu reduzieren. –

+0

auch Sitzung pro Formular macht nicht viel Sinn. Sie wollen wirklich eine Arbeitseinheit. Dies ist in einer Webumgebung einfach, da eine Anfrage eine natürliche Arbeitseinheit ist. Formulare werden dort, wo es ein wenig schwieriger wird, da @DavidOsborne erwähnt, Ihre Use Cases/Stories sollten Ihre Transaktionsgrenzen bestimmen. – Fran

+0

Es lohnt sich auch, die "Sitzung pro Konversation" und verwandte Muster zu untersuchen. –

Antwort

1

Warum die Servicetransaktion nicht beachten?

so etwas wie

protected virtual TResult Transact<TResult>(Func<TResult> func) 
    { 
     if (_session.Transaction.IsActive) 
      return func.Invoke(); 

     TResult result; 
     using (var tx = _session.BeginTransaction(IsolationLevel.ReadCommitted)) 
     { 
      result = func.Invoke(); 
      tx.Commit(); 
     } 

     return result; 
    } 

    protected virtual void Transact(System.Action action) 
    { 
     Transact(() => 
     { 
      action.Invoke(); 
      return false; 
     }); 
    } 

in Ihrem Dienst oder Repo, der überprüft, ob eine Transaktion aktiv ist, und beteiligt sich an einer aktiven oder erstellt eine neue Transaktion, wenn nicht vorhanden ist.

Jetzt können Sie mehrere Serviceanrufe zusammen in einem Gespräch zusammenstellen, anstatt jeden Anruf isoliert zu haben.

+0

Anstelle des Dienstes habe ich beschlossen, stattdessen meine generische Repository-Transaktion zu aktivieren. Dies ist eine zusätzliche Ebene über dem ursprünglichen Repository, aber ich sehe keine andere bessere Lösung. Da es sich um ein generisches Repository handelt, muss weniger Code geändert und verwaltet werden. Vielen Dank. –

Verwandte Themen