それではトランザクション処理の実装に入っていきましょう。.NETでトランザクション処理を行うには、主に次の2つの方法があります。
(1)System.Data.SqlClient名前空間のSqlTransactionクラスを利用する
(2)System.Transactions名前空間のTransactionScopeクラスを利用する
(1)はマニュアル・トランザクションと呼ばれるもので、トランザクション(SqlTransactionオブジェクト)を作成し、SQL文を含んだコマンド(SqlCommandオブジェクト)をトランザクションに参加させ、最後にコミット/ロールバックを行うという、昔ながらのプログラミング・モデルです。
これに対し、(2)は.NET Framework 2.0から利用可能な新しい方法で、自動トランザクション(あるいは「暗黙的なトランザクション」)と呼ばれています。これは、トランザクション(TransactionScopeオブジェクト)を作成するだけで、それ以降に行うSQL処理を自動的にトランザクションに参加させることができます。
今回では、新しくて便利な(はず)の(2)のTransactionScopeクラスを使ってトランザクション処理を行います。本連載では、テーブルアダプタが持つメソッドによってのみデータベース処理を行っていますので(といっても、使っているのはFillメソッドとUpdateメソッドだけですが)、自動的にトランザクションに参加できる(2)が便利です。
■TransactionScopeクラスによるトランザクション
TransactionScopeクラスの使い方は簡単で、大枠は次のようなコーディングになります。
……
' トランザクションの始まり
Using scope As New System.Transactions.TransactionScope()
'
' データベースのアクセス処理
'
scope.Complete() ' コミット
End Using ' トランザクションの終わり
……
このリストでは、Using 〜 End Usingまでの間で実行したデータベースの処理が1つのトランザクションとなります。TransactionScopeクラスをインスタンス化すれば、それがトランザクションの始まりとなります*3。
*3 TransactionScopeクラスのコンストラクタでは、パラメータにより、実行されるコードが参加するトランザクションを制御することもできます。コードが現在参加しているトランザクションは「アンビエント・トランザクション」と呼ばれます。アンビエント(ambient)とは「周囲の」「環境の」という意味です。
トランザクションをコミットするには、Completeメソッドを呼び出します。ロールバックするにはTransactionScopeメソッドのDisposeメソッドを呼び出せばよいのですが、Usingステートメントを使っているので、Using 〜 End Usingを抜けるだけで、自動的にDisposeメソッドが呼び出されます(正確には、Completeメソッドによりトランザクションが成功したことを示し、Disposeメソッド呼び出し時にコミットあるいはロールバックが行われます)。
なお、TransactionScopeクラスはSystem.Transactions.dllで実装されており、このDLLファイルはデフォルトではプロジェクトから参照されていませんので、次の図のようにして参照の追加を行う必要があります。
■サンプル・アプリケーションにおけるTransactionScopeクラスの利用
それではTransactionScopeクラスを利用して、サンプル・アプリケーションにトランザクション処理を実装しましょう。実は環境の問題により、このままでは実行時にエラーとなるのですが、コードは次のようになります。前回で作成したコードに、UsingとEnd Using、そしてコミットを行うための「scope.Complete()」の3行を追加しただけです。
Private Sub 注文BindingNavigatorSaveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 注文BindingNavigatorSaveItem.Click
Me.Validate()
Me.注文BindingSource.EndEdit()
Me.注文明細BindingSource.EndEdit()
Dim rows() As DataRow
' トランザクションの始まり
Using scope As New System.Transactions.TransactionScope()
' 注文明細テーブルの削除処理
rows = Me.NORTHWNDDataSet.注文明細.Select( _
"", "", DataViewRowState.Deleted)
If rows.Length <> 0 Then
Me.注文明細TableAdapter.Update(rows)
End If
' 注文テーブルの削除処理
rows = Me.NORTHWNDDataSet.注文.Select( _
"", "", DataViewRowState.Deleted)
If rows.Length <> 0 Then
Me.注文TableAdapter.Update(rows)
End If
' 注文テーブルの追加・変更処理
rows = Me.NORTHWNDDataSet.注文.Select("", "", _
DataViewRowState.Added Or DataViewRowState.ModifiedCurrent)
If rows.Length <> 0 Then
Me.注文TableAdapter.Update(rows)
End If
' 注文明細テーブルの追加・変更処理
rows = Me.NORTHWNDDataSet.注文明細.Select("", "", _
DataViewRowState.Added Or DataViewRowState.ModifiedCurrent)
If rows.Length <> 0 Then
Me.注文明細TableAdapter.Update(rows)
End If
scope.Complete() ' トランザクションのコミット
End Using ' トランザクションの終わり
End Sub
アプリケーションを実行し、試しに先ほどと同じように注文データを削除してから[データの保存]ボタンをクリックしてみましょう。恐らく2番目のUpdateメソッド呼び出しで、次のようなエラーが出るはずです。
「コンピュータでMSDTCは使用できません」というエラーが出ています。「MSDTC」とは何でしょうか?
MSDTCは「Microsoft Distributed Transaction Coordinator」の略で、Windowsサービスの1つです。日本語では「分散トランザクション・コーディネイタ」と呼ばれます。Windowsのサービス一覧を確認してみると、このサービスが停止状態になっているはずです。
このサービスを開始しておくと、確かにアプリケーションは問題なく動作するわけですが、1台のコンピュータ上ですべてを行っているのに分散トランザクションとは一体何事でしょうか?
Copyright© Digital Advantage Corp. All Rights Reserved.