10 行でズバリ !! TransactionScope の利用 (C#)
マイクロソフト株式会社 デベロッパー & プラットフォーム統括本部
エバンジェリスト 中原 幹雄
最終更新日 2006 年 6 月 23 日
|
|
|
|
このコンテンツのポイント
- System.Transactionsが提供する暗黙的なトランザクション実装機能であるTransactionScopeクラスを使ったトランザクションのプログラミング方法を理解する。
今回紹介するコード
<Form1.cs>using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using TxsSample.AdventureWorksDataSetTableAdapters; // 型付きTableAdapter
using System.Transactions; // System.Transactions
namespace TxsSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Button1 押下時の処理
// テーブルの内容をデータセットに読み込む
private void button1_Click(object sender, EventArgs e)
{
ProductCategoryTableAdapter adapter = new ProductCategoryTableAdapter();
adapter.Fill(adventureWorksDataSet1.ProductCategory);
}
// Button2 押下時の処理
// データセットの内容に基づいてテーブルを更新
private void button2_Click(object sender, EventArgs e)
{
try
{
// TransactionScopeを生成し、トランザクションの範囲を指定
using (TransactionScopenew TransactionScope())
{
// 以下はトランザクションの範囲内:
// トランザクショナルなリソースに対する処理は、
// 暗黙的にトランザクションとして処理される
ProductCategoryTableAdapter adapter =
new ProductCategoryTableAdapter();
adapter.Update(adventureWorksDataSet1.ProductCategory);
// Completeメソッドの呼び出しまでがトランザクションの範囲
// トランザクションをコミットする
tx.Complete();
}
// トランザクション処理成功時のメッセージを表示
MessageBox.Show("更新に成功しました。", "Success",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
// トランザクションがアボートされたときの処理
catch (Exception ex)
{
// トランザクション処理失敗時のメッセージを表示
MessageBox.Show("更新に失敗しました。\r\n" + ex.ToString(), "Failed",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
} 今回のシステム要件
- Visual Studio 2005
- SQL Server 2005 Express Edition
|
|
|
|
|
目次
はじめに
作成するアプリケーションの概要
アプリケーションの作成
動作と解説
おわりに
はじめに
System.Transactionsは.NET Framework 2.0から追加された、トランザクション プログラミングのための新しい統一プログラミング モデルです。ここでは、System.Transactionsが提供する機能の内の代表的なものとして、暗黙的なトランザクション実装機能であるTransactionScopeクラスの使用方法について取り上げます。
作成するアプリケーションの概要
ここでは、TransactionScopeクラスを使用し、トランザクションを使ったデータベースへの更新処理を行うWindowsアプリケーションを作成します。
なお、データベースとしては SQL Server 2005 に付属の 「 AdventureWorks 」 サンプル データベースを使用します。( SQL Server 2005 Express Edition を使用している場合は、ダウンロードセンタ / SQL Server 2005 Express Edition ドキュメントとサンプル より別途入手し、SQL Server 2005 Express Editionにデータベースをアタッチした状態から本手順を進めてください。)
Windows フォームのプロジェクトを作成
Visual Studio 2005 を起動して、新規にプロジェクトを作成します。
ここでは、 [ Visual C# ] - [ Windows アプリケーション ] テンプレートを使用し、プロジェクト名 "TxsSample" にてプロジェクトを作成します。
.gif)
図 1. データの更新のためのプロジェクトを新規に作成する
画面をデザインする
データの表示と更新を行うために、DataGridView コントロールを、ツールボックスから Form1 へドラッグ アンド ドロップします。次に、読み込み用の Button コントロール (Button1) と更新用の Button コントロール (Button2) を順に追加します。それぞれのボタンの Text プロパティを、「読み込み」、「更新」にします。
.gif)
図 2. 画面上にコントロールを配置する
データセットとテーブル アダプタを構成する
次に、データベースへアクセスするためのデータセットとテーブル アダプタを作成します。
まず、[データ] メニューから [データ ソースの表示] をクリックして、図3 のような [データ ソース] ウィンドウを表示します。この後の操作方法は、原則として 『10 行でズバリ !! ADO.NET と Visual Studio 2005 によるデータアクセス入門』 で説明した [データ ソース] ウィンドウの使用方法と同じです。
![図 3. [データ ソース] ウィンドウ](http://i.msdn.microsoft.com/dd253124.Transaction_Scope_cs_fig3(ja-jp,MSDN.10).gif)
図 3. [データ ソース] ウィンドウ
[データ ソース] ウィンドウの左端にある [新しいデータ ソースの追加] ボタンをクリックします。(ボタンの名称は、マウスポインタをボタンの上に移動することで、ツールヒントとして表示されます。) すると、データ ソース構成ウィザードが起動します。(図4)
.gif)
図4. データ ソース構成ウィザード
図4 では、[データ ベース] を選択して、[次へ] ボタンをクリックします。
図5 のように、データ接続を選択する画面になるので、画面の右側にある [新しい接続] ボタンをクリックします。
.gif)
図5 データ接続を選択する
図6 のように、[接続の追加] ダイアログ ボックスが開くので、利用可能なサンプル データベースを指定します。
ここでは、ローカルのSQL Server 2005 Express Edition上のAdventureWorks のデータベースを選択した例を示します。
.gif)
図6. 接続するデータベースを指定する
ここで[OK]ボタンをクリックすると、自動的にデータベースにアクセスするための接続文字列が作成されます (図7)。 [次へ] をクリックします。
.gif)
図7. データベースへの接続文字列を自動構成する
次に、データベースにアクセスするための接続文字列を、アプリケーション構成ファイルに保存するか問い合わせが表示されます (図8)。Visual Studio 2005 では、接続文字列はソース コードに書かずに、アプリケーション構成ファイルに切り分けることが可能です。これによって、アプリケーションの運用時に後から接続先を変えるとき、コード修正や再コンパイルをせずに、構成ファイルを変更するだけで済みます。[次の名前で接続を保存する] チェックボックスをチェックしたままにして、[次へ] をクリックします。
.gif)
図8. 接続文字列は、アプリケーション構成ファイルに保存できる
次に、データベース オブジェクトを選択する画面になりますので (図9)、その画面のツリー上にある [テーブル] ノードを展開し、テーブルとして、[ProductCategory] テーブルのみをチェックし(図9)します。
.gif)
図9. ProductCategoryテーブルを指定する
ウィザードが終了すると、[データ ソース] ウィンドウには、指定したテーブルが表示されます。(図10)
.gif)
図10. データセット AdventureWorksDataSet の構造が表示される
データセットとDataGridViewコントロールを連結する
ここでは、プロパティ ウィンドウを使って、データセットと DataGridView を連結します。プロパティ ウィンドウから連結対象として特定のデータセットを指定する場合、予めフォーム デザイナ環境で、データセット インスタンスを作成しておく必要があります。
ツールボックスの [データ] タブから DataSet をフォームにドロップします。図11のようなダイアログ ボックスが表示されるので、データセットとして 「TxsSample.AdventureWorksDataSet」 が選択されていることを確認し、[OK] をクリックします。このデータセットは、先のデータ ソース構成ウィザードで作ったものです。
.gif)
図11. データセットを追加する
すると、図12のようにデザイナ下部 (トレイ) に、データセットのインスタンスを表すアイコンが貼り付きます。
.gif)
図12. データセットのインスタンスが追加される
これで、特定のデータセットをデータソースとして連結するよう、プロパティ ウィンドウで指定できます。
DataGridView1 コントロールの DataSource プロパティを "adventureWorksDataSet1"、DataMember プロパティを "ProductCategory" に設定します。 (図13)
.gif)
図13. DataGridViewコントロールのDataSourceとDataMemberプロパティの設定
以上で画面のデザインは終了です。
参照設定を追加する
次に、本コンテンツのメインであるSystem.Transactionsを使用するため、System.Transactionsアセンブリをプロジェクトの参照設定に追加します。(図14)
.gif)
図 14. System.Transactionsアセンブリを参照設定に追加
コードを実装する
Form1.cs のコードビューを開き、以下のusingを追加します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using TxsSample.AdventureWorksDataSetTableAdapters; // 型付きTableAdapter
using System.Transactions; // System.Transactions
次に、Windows フォーム デザイナ上で Button1 をダブルクリックして、イベント ハンドラを生成させ、以下のコードを記述します
// Button1 押下時の処理
// テーブルの内容をデータセットに読み込む
private void button1_Click(object sender, EventArgs e)
{
ProductCategoryTableAdapter adapter = new ProductCategoryTableAdapter();
adapter.Fill(adventureWorksDataSet1.ProductCategory);
}このコードでは、ProductCategoryTableAdapter のインスタンスを作成し、データセットに対してデータベースから読み込んだデータを格納しています。
そして、再びWindows フォーム デザイナ上で、Button2 をダブルクリックして、イベント ハンドラを生成させ、以下のコードを記述します。
// Button2 押下時の処理
// データセットの内容に基づいてテーブルを更新
private void button2_Click(object sender, EventArgs e)
{
try
{
// TransactionScopeを生成し、トランザクションの範囲を指定
using (TransactionScope tx = new TransactionScope())
{
// 以下はトランザクションの範囲内:
// トランザクショナルなリソースに対する処理は、
// 暗黙的にトランザクションとして処理される
ProductCategoryTableAdapter adapter =
new ProductCategoryTableAdapter();
adapter.Update(adventureWorksDataSet1.ProductCategory);
// Completeメソッドの呼び出しまでがトランザクションの範囲
// トランザクションをコミットする
tx.Complete();
}
// トランザクション処理成功時のメッセージを表示
MessageBox.Show("更新に成功しました。", "Success",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
// トランザクションがアボートされたときの処理
catch (Exception ex)
{
// トランザクション処理失敗時のメッセージを表示
MessageBox.Show("更新に失敗しました。\r\n" + ex.ToString(), "Failed",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} こちらが本コンテンツのメインとなる、TransactionScopeを使用したトランザクショナルなデータベース更新処理になります。TransactionScopeのインスタンスを生成し、Complete() メソッドが呼び出されるまでがトランザクションの範囲となっています。ここでは再び ProductCategoryTableAdapterを使用し、データセットの内容をデータベースに更新する処理を行っています。また、トランザクションの成功・失敗に対し、それぞれダイアログ ウィンドウを表示し、処理の終了を確
アプリケーションを実行する
それでは、アプリケーションを実行してみます。 [Ctrl] +[F5] キーを押すか、[デバッグ] メニューの [デバッグなしで開始] をクリックします。
フォームが表示されたら、[読み込み] ボタンを押してみます。ProductCategory テーブルのデータが読み込まれ、次のように表示されます。(図15)
.gif)
図 15. アプリケーションを実行する
次に変更を行います。
たとえば、ProductCategoryID が "1" のデータの Name を "Bikes123" に変更します(図16)。
.gif)
図 16. フォーム上のデータを変更する
次に、[更新] ボタンを押して、データベースを更新してみます。以下のように更新が完了した旨のメッセージが表示されます。(図17)
.gif)
図 17. 正常更新されたことが通知された
ここではデータベースへの更新処理がトランザクションとして実行されています。
しかし、このままでは本当にトランザクションとして処理されているのか確認できませんので、意図的にトランザクションをアボートさせるようコードを変更してみましょう。
(以降の内容は通常の実装では不要なコードです。あくまでもトランザクションが動作していることの確認として行っています。)
一旦、アプリケーションを終了して、以下の下線で示しているコード (throw new Exception();) をbutton2_Clickイベントハンドラのコード内に追加してください。
// Button2 押下時の処理
// データセットの内容に基づいてテーブルを更新
private void button2_Click(object sender, EventArgs e)
{
try
{
// TransactionScopeを生成し、トランザクションの範囲を指定
using (TransactionScope tx = new TransactionScope())
{
// 以下はトランザクションの範囲内:
// トランザクショナルなリソースに対する処理は、
// 暗黙的にトランザクションとして処理される
ProductCategoryTableAdapter adapter =
new ProductCategoryTableAdapter();
adapter.Update(adventureWorksDataSet1.ProductCategory);
// Completeメソッドの呼び出しまでがトランザクションの範囲
throw new Exception();
// トランザクションをコミットする
tx.Complete();
}
// トランザクション処理成功時のメッセージを表示
MessageBox.Show("更新に成功しました。", "Success",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
// トランザクションがアボートされたときの処理
catch (Exception ex)
{
// トランザクション処理失敗時のメッセージを表示
MessageBox.Show("更新に失敗しました。\r\n" + ex.ToString(), "Failed",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} このコードの変更により、トランザクション処理の範囲内で、データベースへの更新処理が呼び出されたが、まだトランザクション自体がコミットされてない状態でトランザクションがアボートされたときに、データベースへの更新処理がロールバックされるかどうか、すなわちトランザクション処理として動作しているかどうかを確認することができます。 それではもう1度アプリケーションを実行しましょう。先ほどの実行でデータベースを更新したため、ProductCategoryID が "1" のデータの Name が "Bikes123" に変更されています。今度は元に戻すためName を "Bikes" に変更します。(図18) .gif)
図18. 再度フォーム上のデータを変更 そして、[更新] ボタンを押して、データベースを更新してみます。今度は以下のように更新が失敗した旨のメッセージが表示されます。(図19) .gif)
図 19.更新に失敗したことが通知された トランザクションとして処理されているのであれば、今の失敗した更新処理の内容はデータベースには反映されていないはずです。もう一度[読み込み] ボタンを押して、現在のデータベース情報を取得してみます。(図20) .gif)
図 20.更新に失敗したことが通知された すると、ProductCategoryID が "1" のデータの Name は "Bikes123" となっています。これは、先ほどのトランザクション処理中に失敗した更新処理の内容は反映されておらず、ロールバックされていることを示しています。 これでTransactionScopeによるトランザクション処理が機能していることが確認できました。 このように、2つのプログラミング モデルには大きな違いがあり、後で使用するプログラミング モデルを変更することは難しく、トランザクションを扱うアプリケーションを開発際には、将来の機能拡張に対する予測 (将来的に分散トランザクションが必要か否か) も含め、どちらのプログラミング モデルを使用するのかを予め慎重に検討する必要がありました。
System.Transactions、はこうした既存の2つのプログラミング モデルの違いを吸収し、より容易に分散トランザクション プログラムを開発可能にするための統一プログラミング モデルとして提供されました。System.Transactionsでは、MSDTCを利用した分散トランザクションを、Enterprise Services のようにCOM+を意識した複雑な仕組みを必要とせずに実装することが可能になります。 System.Transactionsは「トランザクションの昇格 (プロモーション)」と呼ばれるメカニズムをサポートしており、このメカニズムにより、単一のリソースに対するローカル トランザクションであっても、複数のリソースをまたぐ分散トランザクションであっても、同じプログラミング モデルによって実装することが可能になっています。 この「トランザクションの昇格」は、トランザクションの範囲内の対象リソース数に応じて自動的にトランザクションの種類を適切に選択するメカニズムであり、対象リソースが1つしか存在しない場合にはローカル トランザクションとして、対象リソースが複数存在する場合には分散トランザクションとして実行されます。 このメカニズムによって、Enterprise Servicesの欠点であった単一リソースに対するトランザクション処理の性能的な (単一リソース時にも分散トランザクションのメカニズムが使用されてしまう) 問題を解消し、手動トランザクションに限りなく近いレベルの性能でローカル トランザクションを実行することが可能になっています。 なおTransactionScopeは、System.Transactionsで提供されているトランザクションの実行管理機能の代表的な機能であり、トランザクションに対して明示的にリソースを登録する必要の無いことから「暗黙的なトランザクションの実装」と呼ばれています。 System.Transactionsの詳細については、下記の技術情報を参照ください。 「 .NET Framework 2.0 の System.Transactions について 」 |
おわりに このように、System.TransactionsのTransactionScopeクラスを使用することで、トランザクションに対応したリソース(このサンプルではADO.NETを介したSQL Serverデータベース) に対するトランザクション処理が自動的に行われ、トランザクション制御ロジックを非常にシンプルに実装することができます。 なお、今回のサンプルでは1つのデータベースのみをトランザクションの対象としているため、ローカル トランザクションとして実行されますが、TransactionScopeで指定したトランザクションの範囲内 (コードブロック内) に別のデータベースに対するアクセス処理を追加すると、自動的に2つのデータベース間をまたぐ分散トランザクションとして実行されます。 これからのアプリケーション開発に、是非System.TransactionsならびにTransactionScopeをご活用ください。 |
ページのトップへ