さて、Silverlight 2 や WCF などの最新テクノロジの話ばっかりここまで書いてきたので、たまには地味(けれどもめちゃめちゃ重要)な話をひとつ書いてみたりします。結論を先に書くと、以下の通りです。
「SQL Server 2008 と .NET Framework 2.0 SP1 を使うと、テーブルアダプタと TransactionScope を組み合わせる際の昇格条件が緩和される(=MS-DTC 昇格せずに済むことがある)」
具体的には、以下のようなコードで MS-DTC 昇格が発生しなくなります。
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
PubsDataSetTableAdapters.QueriesTableAdapter ta = new PubsDataSetTableAdapters.QueriesTableAdapter();
// SELECT price FROM titles WITH (UPDLOCK) WHERE title_id=@title_id
decimal? price = ta.GetPriceByTitleIdWithUpdlock("BU1032");
decimal newPrice = price.Value + 1;
// UPDATE titles SET price=@newPrice WHERE title_id=@title_id
ta.SetNewPriceByTitleId(newPrice, "BU1032");
scope.Complete();
}
これがどれぐらい大きなインパクトなのかは、テーブルアダプタと TransactionScope に慣れ親しんでいる方であればすぐにご想像がつくかと思うのですが(というかこの機能をどれほど待ち望んだことか...!)、あまりご存じない方もいらっしゃると思いますので、以下に少し詳しく説明します。
※ なお、自動トランザクション(TransactionScope)とテーブルアダプタの基本についてはここでは解説しませんので、拙著(Visual Studio 2005 による Web アプリケーション構築技法)あたりを読んでみていただけると助かります。
[自動トランザクション(TransactionScope)と MS-DTC の関係について]
TransactionScope は、コネクションの外側からトランザクションを制御するための技術で、これを使うと、複数の物理接続から行われたデータベース読み書きを、ひとつのトランザクションに束ねることができます。
具体的な使い方も非常に簡単で、下図に示すように、TransactionScope を使った using ブロックの内側で、「個々にデータベース接続を開き、データを読み書きし、接続を閉じる」という処理を書くだけです。このようにすると、個々のデータベースに対する読み書きをひとつのトランザクションに束ねることができました。
一般的に、このような「複数のデータベースに対する読み書き処理」をひとつのトランザクションに束ねるためには、2 相コミット(2 Phase Commit, 以下では 2PC と略します)と呼ばれる制御が必要です。この 2 相コミット制御を行っているのが、Windows OS のサービスの一つである MS-DTC(Microsoft Distributed Transaction Coordinator)です。TransactionScope オブジェクトを使うと、このオブジェクトが MS-DTC サービスとの連携動作を行い、MS-DTC サービスがこの 2 相コミット制御を行ってくれます。
さて、この自動トランザクション(TransactionScope)という技術は、以下の 2 つの観点から重要です。
ここで問題になるのは、後者の目的で TransactionScope を使う場合です。例えば、ビジネスロジッククラス(BC)の中からデータアクセスクラス(DAC)を切り出そうと思う際には、TransactionScope を使うと、下図のように非常に簡単に DAC を切り出すことができます。
ところが、自動トランザクションの内部制御には MS-DTC が使われているため、上図のように、スコープ内で一つのデータベースしか利用していない場合でも MS-DTC が動作してしまい、不要なオーバヘッドが発生してしまう、という問題があります。
[自動トランザクション(TransactionScope)における昇格動作(Promotion 動作)について]
こうした問題を避けるため、(.NET Framework 2.0 と SQL Server 2005 では)、以下の 2 つの条件を満たす場合には、TransactionScope を使っても MS-DTC が動作しないようになっていました。
この動作は、.NET Framework 2.0 と SQL Server 2005 の 2 つの連携動作によって実現されており、その肝になっているのが以下の 2 つでした。
もう少し詳しく説明すると、以下のようになります。
つまり、TransactionScope を使う場合は、最初から MS-DTC を使うことが確定しているわけではなく、「とりあえず MS-DTC を使わないですむのならがんばってみる」という挙動をし、ダメだとわかった時点で MS-DTC を使う分散トランザクションに昇格する、という動作をします。(このため、TransactionScope は Promotable Transaction (昇格可能トランザクション)とも呼ばれています。)
[テーブルアダプタと TransactionScope を組み合わせた場合の動作について]
がしかし、現実的な話をすると、前述のような昇格動作機能は事実上宝の持ち腐れ、というのが実際のところでした。というのも、実際の開発現場では、TransactionScope は Visual Studio 2005 でサポートされたテーブルアダプタと組み合わせて使われることが多く、その場合には、物理コネクションが一本で済むということが事実上なかったからです。
このことを具体的に示すために、ちょっとしたサンプルアプリケーションを作ってみたいと思います。(例によって pubs.mdf ファイルはどっかから入手してください。)
TableAdapter クエリの構成ウィザードが開いたら、以下の作業を行います。
さらに Program.cs ファイルに戻り、以下の作業を行います。
Console.WriteLine("価格を更新しました。" + newPrice.ToString());
できあがったらこれを Ctrl + F5 で動作確認します。
さて、これだけだと MS-DTC が動作したかどうかがわからないので、以下の 2 つの作業を行います。
static void Main(string[] args)
TransactionManager.DistributedTransactionStarted += new TransactionStartedEventHandler(TransactionManager_DistributedTransactionStarted);
static void TransactionManager_DistributedTransactionStarted(object sender, TransactionEventArgs e)
Console.WriteLine("MS-DTC トランザクションへの昇格が発生しました。");
この状態でアプリケーションを実行すると、MS-DTC が動作している様子が確認できます。MS-DTC が動作するのは、以下のような理由によります。
[SQL Server 2008 と .NET Framework 2.0 SP1 を使った場合について]
しかし、前述のアプリケーションコードを考えた場合、物理的には同一のデータベースを利用しているわけですから、MS-DTC による分散トランザクション処理ではなく、LCT によるローカルトランザクション処理を行ってほしい、と思うのが人情でしょう。で、これが SQL Server 2008 と .NET Framework 2.0 SP1 を使うことにより可能になりました。
つまり、前述のアプリケーションを SQL Server 2008 に対して使うと、下記のように DTC 昇格が発生しません(=ローカルトランザクションと同等のパフォーマンスで処理されます)。(接続文字列を書き替えて SQL Server 2008 に対して処理を行うようにするか、または SQL Server 2005 Express Edition を SQL Server 2008 Express Edition にアップグレードしてください。)
このような挙動を行えるようになった詳細な理由は、以下にまとめられています。
が、読むのは大変だと思うので、キーポイントをまとめると以下のようになります。
この機能強化は、地味ですが極めて重要です。というのも、テーブルアダプタと TransactionScope を使う開発は極めて高開発生産性・高保守性なのですが、今までは MS-DTC が動作してしまう=性能的なオーバヘッドがある、というデメリットで敬遠する人がいました。しかし、SQL Server 2008 と .NET Framework 2.0 SP1 を使っていただくと、この問題が回避できることになります。
※ 実際には MS-DTC のオーバヘッドはそれほど大きくなく、取引システムなど特に性能を重視するようなアプリケーションでなければまず問題になることはないのですが、「ちょっとでも性能が悪くなる」という理由で極端に嫌うような人がいたことも事実です。。。
なお、本機能について、いくつか注意事項をまとめておきます。
この機能は、Visual Studio 2005 & .NET Framework 2.0 で自動トランザクションとテーブルアダプタが出た当初から待望していたのですが、ようやくこうした形でサポートされるようになったのはうれしい限りです。これがあると、テーブルアダプタ+自動トランザクションの組み合わせによる開発方法の適用範囲が今まで以上に増えるでしょうね。
[2009/01/23 追記]
本件ですが、MSDN Library に情報が出ましたので追記しておきます。興味がある方はこちらをどうぞ。