鉄壁のデータ整合性を求めて:VB.NETにおけるTransactionScopeの深淵と実践的実装
システム開発の現場において、「トランザクション」という言葉は安易に使われがちだ。しかし、真のアーキテクトにとって、それは「障害発生時にシステムを無垢な状態へ回帰させるための最後の防波堤」に他ならない。
特に、レガシーなVB6/VBAから.NETへとモダナイズを進める際、最も頭を悩ませるのが「複数テーブル、あるいは複数DBに跨る整合性」だ。ここでは、.NETの強力な武器である`System.Transactions.TransactionScope`を軸に、メモリとリソースを極限まで制御する実装の真髄を説く。
—
1. なぜ「手動トランザクション」ではなく「TransactionScope」なのか
VB.NETにおいて、`SqlTransaction`を用いた手動制御は、単一の接続には有効だ。しかし、接続先が複数あったり、サービス層をまたぐ複雑なビジネスロジックが存在する場合、手動管理はコードを汚染し、メンテナンス不能な「スパゲッティ・トランザクション」を招く。
`TransactionScope`は、暗黙的なトランザクション境界を定義する。これにより、スコープ内で発生した複数の接続が、自動的に分散トランザクション(MSDTC)へと昇格、あるいはローカルであれば最適化される。この「抽象化された整合性」こそが、業務システムの安定性を担保する。
—
2. 鋼鉄のトランザクション実装:コードの核心
以下のコードは、単なるコピペ用ではない。メモリ効率を考慮し、かつ例外発生時にリソースを確実に解放するための「定石」である。
Imports System.Transactions
Imports System.Data.SqlClient
”’
”’
Public Sub ExecuteBusinessTransaction(connectionStringA As String, connectionStringB As String)
‘ トランザクションオプションの精査(タイムアウトや分離レベルを明示的に指定せよ)
Dim options As New TransactionOptions With {
.IsolationLevel = IsolationLevel.ReadCommitted,
.Timeout = TimeSpan.FromSeconds(30)
}
‘ using句でスコープを定義。これにより自動的にDisposeが担保される
Using scope As New TransactionScope(TransactionScopeOption.Required, options)
Try
‘ 接続1: 基幹DB
Using connA As New SqlConnection(connectionStringA)
connA.Open()
ExecuteQuery(connA, “UPDATE Inventory SET Stock = Stock – 1 WHERE ID = 1”)
End Using
‘ 接続2: ログDB/外部DB
Using connB As New SqlConnection(connectionStringB)
connB.Open()
ExecuteQuery(connB, “INSERT INTO AuditLog (Action) VALUES (‘StockDecremented’)”)
End Using
‘ 全ての操作が成功した場合のみ完了をマーク
‘ これを忘れると、例外が発生しなくともロールバックされる(非常に重要)
scope.Complete()
Catch ex As Exception
‘ ここでログを吐く際は、重い例外処理を避け、スタックトレースを確実に残す
Logger.Error(“トランザクション失敗: ” & ex.Message)
Throw ‘ 上位層へ例外を再スローし、異常系であることを伝播させる
End Try
End Using
End Sub
—
3. シニアエンジニアが知るべき「落とし穴」
コードを動かすだけなら初学者でもできる。しかし、現場で生き残るには以下の知見が不可欠だ。
① MSDTCの恐怖
`TransactionScope`が異なる物理サーバーや異なる種類の接続に対して作用する場合、WindowsのMSDTC (Microsoft Distributed Transaction Coordinator) サービスが必要になる。ファイアウォール設定やRPCポートの開放といったインフラ要件を疎かにすれば、本番環境で確実にクラッシュする。
② usingの重要性とGCの挙動
VB.NETの`Using`は、C#と異なりDisposeを確実に呼ぶための必須作法だ。特にDB接続のようなアンマネージドリソースに近いオブジェクトは、ガベージコレクション(GC)の気まぐれを待ってはいけない。スコープから抜ける瞬間に明示的に解放せよ。
③ 接続のオープン時期
`TransactionScope`の内部では、接続を「開いた瞬間」にトランザクションへ参加する。不要なタイミングで接続を開かず、極力、トランザクションの直前で接続を開くことで、デッドロックの発生確率を数学的に最小化できる。
—
4. 総括:レガシーからの脱却を志す者へ
VB6時代、「On Error GoTo」で強引にロールバックを行っていた諸君。`.NET`の世界では、トランザクションは「コードの記述」ではなく「スコープの管理」へと進化した。
システムは常に「失敗するもの」として設計せよ。`TransactionScope`による境界の明確化は、将来的なマイクロサービス化や疎結合なアーキテクチャへの移行に対する、最も強力な布石となる。
「動く」ことは最低条件だ。「壊れない」ことこそが、エンジニアのプライドである。
このコードが、貴殿のシステムの堅牢性を一段階引き上げることを確信している。
