.NETエンタープライズ
Webアプリケーション開発技術大全

接続型データアクセスと短時間トランザクション

マイクロソフト コンサルティング本部 赤間 信幸
2005/03/15


3.4 自動トランザクション

 逆に、マニュアルトランザクションでは記述しづらい複数のクラスにまたがるSQL処理の1トランザクション化や、マニュアルトランザクションでは記述することのできない複数のデータベースにまたがるSQL処理の1トランザクション化が求められる場合には、以下に解説する自動トランザクションを利用するとよい。

 自動トランザクションの概要を図3-9に示す。自動トランザクションでは、Windows OSの基盤サービスの1つである分散トランザクションコーディネータ(MS-DTC)との連携処理が行われる。これによって生成されるトランザクションコンテキストを利用すると、当該トランザクションコンテキスト内から実施されたすべてのSQL処理を1つのトランザクションに束ねてしまうことができる。

図3-9 自動トランザクションの例

 なお、図3-9はアプリケーション設計としては適切なものではないが※11、ここでは他のトランザクション方式との比較のために最も簡単な例として用いることとした。詳細は本書第2部「自動トランザクション処理」にて解説するので、まずは自動トランザクション制御の大まかな全体像を掴んで頂きたい。

※11 図3-9ではユーザインタフェースコンポーネントがトランザクションコンテキストを生成しているが、これは設計観点からすると不適切である。本来は、ビジネスロジックコンポーネントからトランザクションコンテキストを生成すべきである。

3.4.1 実装方法

 自動トランザクションを実装する最も手軽な方法は、ASP.NET Webページのページディレクティブを用いてトランザクションコンテキスト生成を指定し、ContextUtilクラスを用いてコミットとアボートを指示するというものである。

A. 自動トランザクションのコード例

 行うべき作業は以下の3つである(前述の通り、ここに示すコードは設計的に見て不適切なものであるため、スペースの都合上、C#のコードのみ示しておく)。

  • .aspxのページディレクティブの1つであるtransaction属性としてRequiresNewを指定する(図3-10およびリスト3-4)。

  • このページから呼び出される下位のクラスやメソッド内部では、マニュアルトランザクション制御は不要である(リスト3-5)。適宜、コネクションをオープンし、SQL文を発行し、コネクションをクローズするだけでよい。

  • ページ処理を完了する前に、コミットまたはアボート(ロールバック)を指定する(リスト3-6)。

図3-10 トランザクション属性の指定によるトランザクションコンテキストの生成
 
.aspxファイル

<%@ Page language="c#" Codebehind="WebForm1.aspx.cs"
    AutoEventWireup="falseInherits="WebApplication1.WebForm1"
    Transaction="RequiresNew" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
   <HEAD>
... (後略)
リスト3-4 ページディレクティブによるトランザクション属性の指定
 
C#の場合

public bool Debit(string accoutId, int amount)
{
   SqlConnection sqlcon = new SqlConnection(
      ConfigurationSettings.AppSettings["BankA.connectionString"]);
   SqlCommand sqlcmd = new SqlCommand("UPDATE Account SET balance = balance - " + "@amount WHERE id = @id AND balance >= @amount", sqlcon);

   sqlcmd.Parameters.Add("@id", accoutId);
   sqlcmd.Parameters.Add("@amount", amount);

   try
   {
      sqlcon.Open(); // トランザクション制御の記述は不要
      int result = sqlcmd.ExecuteNonQuery();
      if (result == 1)
      {
         return true;
      }
      else
      {
         return false;
      }
   }
   finally
   {
       sqlcon.Close();
   }
}
リスト3-5 下位のクラスやメソッド内でのSQL文の発行
 
.aspx.csファイル

using System.EnterpriseServices;
// System.EnterpriseServices.dll への参照設定も必要

private void btnTransfer_Click(object sender, System.EventArgs e)
{
   // (前略、入力データのチェックなどを実施)
   // 下位コンポーネントを使って、出金と入金をそれぞれ実施する
   bool result1 = accountDac1.Debit(fromId, amount);
   bool result2 = accountDac2.Deposit(toId, amount);
   // それぞれの処理結果を確認し、両方が成功した場合に限ってコミット
   if (result1 && result2)
   {
      lblResult.Text = "振り替え処理を正しく完了しました。";
      ContextUtil.SetComplete();
   }
   else
   {
      lblResult.Text = "振り替え処理ができなかったため、処理を元に戻しました。";
      ContextUtil.SetAbort();
   }
}
リスト3-6 ページ処理終了直前のコミットおよびアボート

B. 自動トランザクションの実装のポイント

 リスト3-5のサンプルコードからもわかるように、下位のクラスやメソッドの中ではトランザクションに関する記述(BeginTransaction( )やCommit( )命令の発行)は一切行う必要がない。ADO.NETのSqlConnectionオブジェクトは、自身がトランザクションコンテキスト内でオープンされた場合には、自動的に当該トランザクションに参加する機能を備えているからである。

 このため自動トランザクションを用いれば、マニュアルトランザクションでは記述しにくかった、複数のクラスあるいは複数のメソッドにまたがるSQL処理の1トランザクション化を容易に実現できる。

C. 複数のデータベースにまたがるSQL処理の1トランザクション化

 また自動トランザクションでは、複数のデータベース(例えば2台以上のSQL Server)に対して行われた処理を1トランザクションに束ねることもできる。さらにMSMQに対する読み書きも、データベースの読み書きとまとめて1つのトランザクションに束ねてくれる。これはマニュアルトランザクションでは記述することができない。

 自動トランザクションを用いた場合、実際のコミット/アボート制御は分散トランザクションコーディネータであるMS-DTCが行う。例えば、トランザクションコンテキスト内から複数のデータベースに対してSQL処理が行われていた場合には、分散トランザクションコーディネータは各々のデータベースシステムと2相コミット(2PC, 2 Phase Commit)処理と呼ばれる調整作業を実施する。これにより、すべてのデータベース書き込みがまとめてコミットされるか、あるいはアボートされるようになっている※12

※12 2相コミットの詳細やその実施タイミングに関しては、「第2部自動トランザクション処理」で詳細に解説する。

3.4.2 メリットとデメリット

 他のトランザクション制御方式と同様、自動トランザクション制御方式にもメリットとデメリットがある。

A. 主なメリット

 自動トランザクション制御方式を利用すると、マニュアルトランザクション制御方式の問題であった、「1つのデータベース、1つのメソッド内に閉じたトランザクション処理の記述」という制限を打ち破ることができるようになる。

 また、第2部で解説するサービスコンポーネントと呼ばれる機能を利用すると、複雑なトランザクションコンテキスト構造を作り、各コンテキストを個別にコミットしたりアボートしたりすることもできる。

 このため自動トランザクションをうまく利用すれば、クラス分割方法や役割分担などの設計自由度を高められるようになる。保守性の高いアプリケーションを設計するためには適切なクラス分割と役割分担が欠かせないが、自動トランザクションはそれを下支えする技術であるといえる。

B. 主なデメリット

 実際のシステム開発プロジェクトで自動トランザクションを利用する際の最も大きな問題点は、設計難易度の高さである。詳細は第2部で解説するが、自動トランザクションを利用するためには、自動トランザクションの内部制御機構の正しい理解が不可欠である上に、コンポーネント設計(クラス分割)とトランザクション設計(ロックの制御やトランザクションコンテキストの組み方)を同時に考える必要がある。また、COM+カタログやGACへの事前登録に関連する作業上の煩わしさもある。

 データベーストランザクションやマニュアルトランザクションに比べると、設計の自由度が高い分、難易度も高く、安易に手を出すと危険である※13。実際のプロジェクトで自動トランザクションを利用する際には、少なくとも以下の2項目を満たしておく必要がある。

  • 本書「第2部 自動トランザクション処理」で解説している内容の完全な理解

  • コンポーネント設計とトランザクション設計のパターン化

※13 実際、設計難易度の高さから自動トランザクションの適用を見送ったプロジェクトも数多くある。逆に、自動トランザクションを十分に理解せずに利用したため、トランザクション制御が破綻して失敗したプロジェクトもある。

 また自動トランザクションを利用するためには、データベースやその接続ドライバが分散トランザクションコーディネータ(MS-DTC)に対応していることも必要である。利用するデータベースがSQL Serverの場合には問題ないが、OracleやDB2などを利用する場合、利用する接続ドライバの種類やバージョンによってはMS-DTCに対応していない場合がある。

 以上のような理由から、自動トランザクションによる短時間トランザクション制御は、以下のような特性を持つアプリケーションに適していると言える※14

  • 複数のデータベースへのアクセスを1トランザクション化する必要があるアプリケーション

  • 複雑なクラス分割やレイヤ構造設計を必要とする大規模なアプリケーション

※14 なお自動トランザクションを利用する場合、トランザクションのコミットやアボートの制御をMS-DTCが実施するため、若干のパフォーマンスオーバヘッドがある。しかし実際の業務アプリケーションでは、「処理全体の時間」に比べると、MS-DTCによるパフォーマンスオーバヘッドはさほど大きくないことも多い。このため業務アプリケーションの特性によって、このオーバヘッドがどの程度致命的になるのかが異なってくる。この点については、本書「第7章 サービスコンポーネントを用いたアプリケーション設計時の注意点」にて議論する。


 INDEX
  .NETエンタープライズWebアプリケーション 開発技術大全
  接続型データアクセスと短時間トランザクション
    1.業務的なトランザクション制御
    2.データベーストランザクション
    3.マニュアルトランザクション
  4.自動トランザクション
    5.短時間トランザクション処理の3つの制御方式の比較
 
インデックス・ページヘ  「.NETエンタープライズWebアプリケーション開発技術大全」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間