TransactionScope のネスト
System.Transactions.TransactionScope は Complete を呼ばない限り Dispose されたときにロールバックしてくれる。 テストとか例外処理とかで便利だ。
using (var ts = new TransactionScope()) { // DB に書き込み ts.Complete(); }
ネストしてもつかえる。このときネスト内で new された TransactionScope すべてが Complete されてはじめてコミットされる。
void WriteParent(string connectionString) { using (var ts = new TransactionScope()) { WriteChild1(); // この時点ではコミットされない WriteChild2(); // まだ ts.Complete(); // ここでようやくコミット } } void WriteChild1() { using (var ts = new TransactionScope()) { // 書き込み ts.Complete(); } } void WriteChild2() { using (var ts = new TransactionScope()) { // 書き込み ts.Complete(); } }
ここで、子の TransactionScope が Complete されずに Dispose されるとその時点でロールバックされるので、親で Complete しようとすると TransactionAbortedException が発生してしまう。 例えば子で以下のように「書き込みが発生するのは特定の場合だけだから、そうじゃなかったら Complete 呼ばない」とかすると NG。
using (var ts = new TransactionScope()) { if(shouldUpdate) { // 書き込み ts.Complete(); } }
(TransactionScope はコンストラクターで新規に作るか既存を流用するを指定できる (TransactionScopeOption) 。このへんの指定で挙動が変わるかも?)