読者です 読者をやめる 読者になる 読者になる

TransactionScope (2)

.NET

これ の続き。TransactionScope の動作についてのメモ。

使用するテーブル

CREATE TABLE Table1 (
    ID INT PRIMARY KEY,
    NAME NVARCHAR(10)
)

ネストしない場合

メソッド Insert は、DBへの接続をオープンして Table1 にデータを追加後接続をクローズする。

using (TransactionScope root = new TransactionScope(TransactionScopeOption.Required)) {
    this.Insert(1, "name1");
    this.Insert(2, "name2");
}
--
RESULT
|  ID  |  NAME  |
| NULL |  NULL  |

これは予想通り。
Complete を予想外の場所で呼ぶと

using (TransactionScope root = new TransactionScope(TransactionScopeOption.Required)) {
    root.Complete();
    this.Insert(1, "name1");// A
    this.Insert(2, "name2");// B
}
--
RESULT
|  ID  |  NAME  |
| NULL |  NULL  |

A で InvalidOperationException がスローされる。
さらに Complete の場所を変えると

using (TransactionScope root = new TransactionScope(TransactionScopeOption.Required)) {
    this.Insert(1, "name1");// A
    root.Complete();
    this.Insert(2, "name2");// B
}
--
RESULT
|  ID  |  NAME  |
|  1   |  name1 |

今度は B で InvalidOperationException がスローされるが、A の Insert はコミットされている。

ネストした場合

普通の使い方はこんな感じかな。

using (TransactionScope root = new TransactionScope(TransactionScopeOption.Required)) {
    this.Insert(1, "name1");// A
    using (TransactionScope nest = new TransactionScope(TransactionScopeOption.Required)) {
        this.Insert(2, "name2");// B
        nest.Complete();
    }
    root.Complete();
}
--
RESULT
|  ID  |  NAME  |
|  1   |  name1 |
|  2   |  name2 |

ネストされた TransactionScope の Complete をコメントアウトしてみる。

using (TransactionScope root = new TransactionScope(TransactionScopeOption.Required)) {
    this.Insert(1, "name1");// A
    using (TransactionScope nest = new TransactionScope(TransactionScopeOption.Required)) {
        this.Insert(2, "name2");// B
        //nest.Complete();
    }
    root.Complete();
}// C
--
RESULT
|  ID  |  NAME  |
| NULL |  NULL  |

すると C で System.Transactions.TransactionAbortedException がスローされる。
今度はルート TransactionScope の Complete をコメントアウトしてみる。

using (TransactionScope root = new TransactionScope(TransactionScopeOption.Required)) {
    this.Insert(1, "name1");// A
    using (TransactionScope nest = new TransactionScope(TransactionScopeOption.Required)) {
        this.Insert(2, "name2");// B
        nest.Complete();
    }
    //root.Complete();
}// C
--
RESULT
|  ID  |  NAME  |
| NULL |  NULL  |

ネストされた TransactionScope の Complete は無視される。MSDN で全ての TransactionScope の Complete が呼ばれた場合のみコミットされると書いてあったので、まあ予想通り。
この状態でネストされた TransactionScopeOption を変えてみると

using (TransactionScope root = new TransactionScope(TransactionScopeOption.Required)) {
    this.Insert(1, "name1");// A
    using (TransactionScope nest = new TransactionScope(TransactionScopeOption.RequiresNew)) {
        this.Insert(2, "name2");// B
        nest.Complete();
    }
    //root.Complete();
}// C
--
RESULT
|  ID  |  NAME  |
|  2   |  name2 |

今度は Complete のコメントアウトを逆にしてみる。

using (TransactionScope root = new TransactionScope(TransactionScopeOption.Required)) {
    this.Insert(1, "name1");// A
    using (TransactionScope nest = new TransactionScope(TransactionScopeOption.RequiresNew)) {
        this.Insert(2, "name2");// B
        //nest.Complete();
    }
    root.Complete();
}// C
--
RESULT
|  ID  |  NAME  |
|  1   |  name1 |