Wie setzt TransactionScope Transaktionen zurück?

99

Ich schreibe einen Integrationstest, bei dem ich eine Reihe von Objekten in eine Datenbank einfüge und dann überprüfe, ob meine Methode diese Objekte abruft.

Meine Verbindung zur Datenbank erfolgt über NHibernate ... und meine übliche Methode zum Erstellen eines solchen Tests besteht darin, Folgendes zu tun:

NHibernateSession.BeginTransaction();

//use nhibernate to insert objects into database
//retrieve objects via my method
//verify actual objects returned are the same as those inserted

NHibernateSession.RollbackTransaction();

Ich habe jedoch kürzlich von TransactionScope erfahren, das anscheinend genau für diesen Zweck verwendet werden kann ...

Ein Beispielcode, den ich gefunden habe, lautet wie folgt:

public static int AddDepartmentWithEmployees(Department dept)
{

    int res = 0;

    DepartmentAdapter deptAdapter = new DepartmentAdapter();
    EmployeeAdapter empAdapter = new EmployeeAdapter();
    using (TransactionScope txScope = new TransactionScope())
    {

        res += deptAdapter.Insert(dept.DepartmentName);
        //Custom method made to return Department ID 
        //after inserting the department "Identity Column"
        dept.DepartmentID = deptAdapter.GetInsertReturnValue();
        foreach(Employee emp in dept.Employees)
        {

            emp.EmployeeDeptID = dept.DepartmentID;
            res += empAdapter.Insert(emp.EmployeeName, emp.EmployeeDeptID);

        }
        txScope.Complete();

    }
    return res;

}

Ich glaube, wenn ich die Zeile nicht einbeziehe txScope.Complete(), werden die eingefügten Daten zurückgesetzt. Aber leider verstehe ich nicht, wie das möglich ist ... wie verfolgt das txScopeObjekt die deptAdapterund empAdapterObjekte und ihre Transaktionen in der Datenbank.

Ich habe das Gefühl, dass mir hier ein paar Informationen fehlen ... Kann ich meine BeginTransaction()und RollbackTransaction() Anrufe wirklich ersetzen, indem ich meinen Code mit umhülle TransactionScope?

Wenn nicht, wie funktioniert dann das TransactionScopeZurücksetzen von Transaktionen?

Mezoid
quelle
Ich habe NHibernate noch nie verwendet, aber vielleicht hilft Ihnen dieser Link .
Wenn Sie nach einer besseren Möglichkeit suchen, Ihre NHibernate-Sitzungen zu verwalten, um Vorgänge in Transaktionen zu gruppieren, sollten Sie meinen Blog-Beitrag zu diesem Thema lesen. Dotnetchris.wordpress.com/2009/01/27/…
Chris Marisic

Antworten:

107

Im Wesentlichen verfolgt TransactionScope nicht die Adapter, sondern die Datenbankverbindungen. Wenn Sie eine DB-Verbindung öffnen, prüfen die Verbindungen, ob eine Umgebungstransaktion (Transaktionsbereich) vorliegt, und melden Sie sich gegebenenfalls bei dieser an. Achtung Wenn mehrere Verbindungen zu demselben SQL Server bestehen, wird dies zu einer verteilten Transaktion eskalieren.

Was passiert, wenn Sie einen using-Block verwenden, stellen Sie sicher, dass dispose auch dann aufgerufen wird, wenn eine Ausnahme auftritt. Wenn also dispose vor txScope.Complete () aufgerufen wird, weist das TransactionScope die Verbindungen an, ihre Transaktionen (oder den DTC) zurückzusetzen.

JoshBerke
quelle
10
TransactionScope verfolgt nur die aktuelle Transaktion im Thread und ändert sie, wenn sie auf dem Modell basieren muss (erfordert, erfordert neues usw. usw.). Die Transaktion benachrichtigt einfach alles, was dazu gehört, nicht nur Datenbankverbindungen.
casperOne
1
Ich denke, das ist nicht ganz richtig. Ich habe eine Teilverfolgung des Quellcodes von TransactionScope durchgeführt und auch diese msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx gesehen, in der steht: "Wenn die Transaktion nicht erstellt wurde, Das Festschreiben erfolgt immer dann, wenn das Festschreiben vom Eigentümer des CommittableTransaction-Objekts aufgerufen wird. Zu diesem Zeitpunkt ruft der Transaktionsmanager die Ressourcenmanager auf und weist sie an, entweder festzuschreiben oder zurückzusetzen, je nachdem, ob die Complete-Methode für das TransactionScope-Objekt aufgerufen wurde. " Die Quellenspur zeigt auch dieses Verhalten an.
user44298
Ich fand diese SO Q & A nützlich: IEnlistmentNotification
Mungflesh
Mir immer noch unklar. Es scheint, dass die Objekte mit Methoden, die innerhalb des Transaktionsbereichs aufgerufen werden, mit dem Transaktionsmechanismus kooperativ sein müssen. Sie müssen nach Commit / Rollback-Benachrichtigungen durch das System suchen und in der Lage sein, sich selbst zurückzusetzen, da sie das Rollback bei Bedarf durchführen (wenn eine Dateilöschung ausgeführt wurde, können wir es offensichtlich nicht auf magische Weise zurücksetzen, es sei denn, wir haben es getan einige Vorsichtsmaßnahmen). Es scheint auch, dass SQL Server-Operationen kooperativ sind. Aber gibt es noch ein anderes kooperatives Objekt in .Net? Und wie schreibt man eine Klasse, die kooperativ ist? Dokumentation?
Minuten
54

Die TransactionScopeKlasse arbeitet mit der TransactionKlasse , die threadspezifisch ist.

Wenn das TransactionScopeerstellt wird, wird überprüft, ob es ein Transactionfür den Thread gibt. Wenn eine existiert, verwendet sie diese, andernfalls erstellt sie eine neue und schiebt sie auf den Stapel.

Wenn ein vorhandener verwendet wird, wird nur ein Zähler für Releases erhöht (da Sie ihn aufrufen müssen Dispose). Wenn bei der letzten Version das Transactionnicht festgelegt wurde, wird die gesamte Arbeit zurückgesetzt.

Der Grund, warum Klassen auf magische Weise über Transaktionen Bescheid wissen, bleibt als Implementierungsdetail für diejenigen Klassen übrig, die mit diesem Modell arbeiten möchten.

Wenn Sie Ihre deptAdapterund emptAdapterInstanzen erstellen , prüfen sie, ob der Thread eine aktuelle Transaktion enthält (die statische CurrentEigenschaft der TransactionKlasse). Wenn dies der Fall ist, registriert es sich bei der Transaction, um an der TransactionFestschreibungs- / Rollback-Sequenz teilzunehmen (die unterschiedliche Transaktionskoordinatoren wie Kernel, verteilt usw. steuert und möglicherweise an diese weiterleitet).

casperOne
quelle
4
Wie funktioniert es mit SqlConnection (s), die innerhalb des Bereichs erstellt wurden? Verwendet die SqlConnection-Klasse intern die Transaction-Klasse, die sich wiederum für das TransactionScope anmeldet? Oder wird es direkt in das TLS aufgenommen?
Kakira