web-dev-qa-db-ja.com

TransactionScopeはどのようにトランザクションをロールバックしますか?

私は、データベースに多くのオブジェクトを挿入し、メソッドがそれらのオブジェクトを取得するかどうかを確認する統合テストを書いています。

データベースへの私の接続はNHibernate経由です...そして、そのようなテストを作成する私の通常の方法は以下を実行することです:

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

しかし、私は最近このことを明らかにしました TransactionScope これは明らかにこのまさに目的に使用できます...

いくつかの 私が見つけたサンプルコード は次のとおりです:

_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;

}
_

txScope.Complete()を含めないと、挿入されたデータはロールバックされます。しかし、残念ながら、それがどのように可能であるか理解できません... txScopeオブジェクトがdeptAdapterおよびempAdapterオブジェクトとそれらのデータベース上のトランザクションをどのように追跡するのか。

ここに少し情報が欠けているように感じます... TransactionScopeを使用してコードを囲むことで、BeginTransaction()および_RollbackTransaction(_)呼び出しを本当に置き換えることができますか?

そうでない場合、TransactionScopeはトランザクションをロールバックするためにどのように機能しますか?

94
mezoid

基本的に、TransactionScopeはアダプターを追跡しません。データベース接続を追跡します。 DB接続を開くと、接続はアンビエントトランザクション(トランザクションスコープ)が存在するかどうかを確認し、存在する場合は参加します。同じSQLサーバーへの接続が複数ある場合、これは分散トランザクションにエスカレートします。

Usingブロックを使用しているため、例外が発生してもdisposeが呼び出されるようになります。したがって、txScope.Complete()の前にdisposeが呼び出されると、TransactionScopeは接続にトランザクション(またはDTC)をロールバックするように指示します。

104
JoshBerke

TransactionScope class は、スレッド固有の Transaction class と連携します。

TransactionScopeが作成されると、スレッドにTransactionがあるかどうかを確認します。存在する場合はそれを使用し、存在しない場合は新しいものを作成してスタックにプッシュします。

既存のものを使用する場合は、リリースのためにカウンターをインクリメントするだけです(その上で Dispose を呼び出す必要があるため)。前回のリリースでは、Transactionがコミットされていなかった場合、すべての作業がロールバックされます。

クラスがトランザクションについて魔法のように知っているように見える理由については、このモデルを使用したいクラスの実装の詳細として残されています。

deptAdapterおよびemptAdapterインスタンスを作成すると、スレッドに現在のトランザクションがあるかどうかがチェックされます(静的 Currentプロパティ on Transactionクラス)。存在する場合は、Transactionに自身を登録して、コミット/ロールバックシーケンス(Transactionが制御し、カーネル、分散、等。)。

52
casperOne