印刷用ページ       送信     
クリックして評価とフィードバックをお寄せください
 Cutting Edge: トランザクション ワークフロー
Related Articles

Udi Dahan が、大規模な Software Plus Services 取引アプリケーションの開発中に、予測していなかった問題を彼らのチームがどのようにして識別し、乗り越えていったのかを説明します。

Udi Dahan

MSDN Magazine April 2009

...

Read more!

Cobra は Python の子孫であり、特に、動的または静的に型指定されたプログラミング モデルを組み合わせて使用することや、組み込みの単体テスト機能、スクリプト機能、およびいくつかの契約による設計の定義が特徴です。そのすばらしい能力をご紹介します。

Ted Neward

MSDN Magazine June 2009

...

Read more!

CLR チームが NET Framework 3.5 SP1 の CLR に対して行った変更と、このサービス パックを使用して既存の CLR 2.0 ベースのアプリケーションを実行した場合のパフォーマンスの向上について説明します。

Surupa Biswas

MSDN Magazine April 2009

...

Read more!

今月は、IronPython を使用して .NET ベースのライブラリを簡単にテストできることを実演します。

James McCaffrey

MSDN Magazine June 2009

...

Read more!

この記事では、CLR を使用したアセンブリのバインドと読み込みに関するベスト プラクティスをいくつか紹介します。

Aarthi Ramamurthy および Mark Miller

MSDN Magazine May 2009

...

Read more!

Also by this Author

今月は、Dino が引き続き動的な Silverlight コンテンツの管理を取り上げ、キャッシュと分離ストレージについて説明します。

Dino Esposito

MSDN Magazine February 2009

...

Read more!

ブラウザ間でイベントの互換性を保持することは簡単な作業ではありません。API を処理する jQuery イベントはブラウザ間のイベント処理の違いに対処するものです。このイベントを使用し、より予測可能な JavaScript を記述できます。

Dino Esposito

MSDN Magazine April 2009

...

Read more!

今月は、Dino Esposito が、Ajax Control Toolkit といくつかの賢いコーディング手法を使用して、Web アプリケーションで Windows スタイルのモーダル ダイアログ ボックスを表示する方法を説明します。

Dino Esposito

MSDN Magazine Launch 2008

...

Read more!

今月は、Dino Esposito が Silverlight のブラウザの相互運用層で Silverlight/Web ページの数多くの対話のニーズに対処する方法を説明します。

Dino Esposito

MSDN Magazine November 2008

...

Read more!

AJAX は、単なる部分的なページ レンダリングを超えた機能の提供を目指しています。ASP.NET AJAX と共に、動的なページが今後どのような方向に進むのかについて、Dino Esposito の考えをお伝えします。

Dino Esposito

MSDN Magazine June 2008

...

Read more!

Popular Articles

This article introduces 10 development tools that can increase your productivity, give you a better understanding of .NET, and maybe even change the way that you develop applications. The tools covered include NUnit to write unit tests, Reflector to examine assemblies, FxCop to police your code, Regulator to build regular expressions, NDoc to create code documentation and five more.

James Avery

MSDN Magazine July 2004

...

Read more!

SQL Server 2005 で正規表現を使用して、効率的で高度なテキスト分析を実行できます。

David Banister

MSDN Magazine February 2007

...

Read more!

The MVP pattern helps you separate your logic and keep your UI layer free of clutter. This month learn how.

Jean-Paul Boodhoo

MSDN Magazine August 2006

...

Read more!

ここでは、新しい F# 言語の基になるいくつかの概念について説明します。F# 言語は、関数型 .NET 言語とオブジェクト指向 .NET 言語の両方の要素を持っています。また、単純なプログラムを記述する方法についても説明します。

Ted Neward

MSDN Magazine Launch 2008

...

Read more!

この記事では、Windows Presentation Foundation でのプログラムおよび宣言によるデータ バインドと表示の手法を説明します。

Josh Smith

MSDN Magazine July 2008

...

Read more!

Cutting Edge
トランザクション ワークフロー
Dino Esposito

コードのダウンロード : : CuttingEdge2007_06.exe (182 KB)
Browse the Code Online
Microsoft® .NET Framework 3.0 の柱である Windows® Workflow Foundation は、.NET ベースのアプリケーションに組み込んだり、対象となる任意のクライアントにサービスとして公開したりすることができるワークフローを開発するためのプログラミング モデル、ランタイム エンジン、およびツールを提供します。では、カスタム ソリューションでワークフローが必要なのは、どのような場合でしょうか。
ワークフローは、ビジネス ロジックおよびルールの頻繁な変更を余儀なくされる場合に間違いなく役立ちます。このようなケースが生じるのは、同じアプリケーションをさまざまな顧客のニーズに合わせてカスタマイズしたり、単純にこの種の柔軟性を顧客から求められたりするからです。また、ワークフローをビジネス プロセス管理 (BPM) サーバーの背後に置き、分散型の複合アプリケーションのモジュールに公開されるビジネス サービスを統制することもできます。さらにワークフローは、企業にまたがるビジネス ロジックの、長期で任意な形式での実装をサポートするためにも役立ちます。
開発者から見た場合、ワークフローはその全体で目的の動作を表す、一連のアクティビティです。現実世界のビジネス ロジックをモデル化するためにワークフローを使用する場合は、必然的にトランザクション タスクを扱うことになります。では、Windows Workflow Foundation でトランザクション セマンティクスをコーディングするには、どうすればよいのでしょうか。
ほとんどのエンタープライズ システムは、特に複雑な問題に対処するために、内部にワークフローを保持しています。また、ほとんどの複雑な問題にはトランザクションが含まれます。処理するトランザクションは大きく分けて 2 種類あります。1 つは、従来的な短期のトランザクションであり、一般に ACID トランザクションと呼ばれます。もう 1 つはグローバルと称される、長期的な、ビジネス全体にまたがるトランザクションです。この種のトランザクションは、ビジネス プロセスのコンポーネントであることが多く、複数の ACID トランザクションで構成されることがあります。これらの ACID トランザクションの成功または失敗は、ワークフローによって表現される、より大きなビジネス プロセスの全体的な結果に影響します。
Windows Workflow Foundation は、ACID トランザクションとビジネス全体にまたがるトランザクションの実装を効果的にするだけでなく、比較的簡単にコーディングおよび更新できるようにする、アドホック アクティビティを提供します。このコラムでは、Windows Workflow Foundation ワークフローに組み込むことができるあらゆる種類のトランザクション タスクを提供する、一連のアクティビティについて取り上げます。

トランザクション タスクの分類
Windows Workflow Foundation ワークフローには、アトミック タスクとして認識されるアクティビティのブロックを 1 つ以上含めることができます。そのようなアクティビティを最も外側のトランザクション ブロックで一度ラップすると、Windows Workflow Foundation ランタイムにより、包含されているすべてのアクティビティが成功、失敗のどちらかであることが保証されます。そのようなトランザクションは、アトミックで (atomic)、一貫性があり (consistent)、独立していて (isolated)、永続性がある (durable) と想定されるため、ACID トランザクションと呼ばれます (図 1)。

機能 説明
アトミック トランザクションのすべての操作が適切に完了するか、どの操作も完了しないかのいずれかです。
一貫性 トランザクションに含まれるリソースは、トランザクションの開始時と終了時に適正な状態である必要があります。トランザクションは、整合性の制約やビジネス ルールに違反することはできません。
独立性 トランザクション内の操作は、他のすべての操作から独立します。トランザクション外部からの操作では、データの中間的な状態を確認することはできません。
永続性 トランザクションが適切に完了すると、その効果は永続化され、取り消すことはできません。
Windows Workflow Foundation ワークフロー全体を、1 つの長期トランザクション (LRT) として扱うこともできます。この場合は、ワークフロー セマンティクスを使用して、複数のサービス、異なるソフトウェア プラットフォーム、およびさまざまな企業が含まれるビジネス プロセスまたはその一部を記述します。LRT トランザクションは、結果がわかるまでに数分、数日、または数週間かかることがあります。プロセスは複数の手順からなる操作を実装し、複数の企業の情報システムにまたがることがあります。通常、LRT トランザクションは、個別に成功または失敗する 1 つ以上の ACID トランザクションで構成されます。しかし、プロセスが長くて複雑なため、プロセス内の後ろの手順が、前の ACID トランザクションの結果と矛盾する状態を引き起こすことがあります。その場合、既にコミットされたトランザクションの結果をロールバックするには遅すぎますが、何らかの方法で影響を補正する必要があります。
"トランザクション" という用語は、一般に ACID プロセスと長期プロセスの両方を表すために使用されますが、これら 2 つの間には明確な違いがあります。トランザクションという用語は、通常は単一の作業単位として処理され扱われる、連続した操作を表します。この操作は通常は数秒で終了し、中断したり、ユーザー入力や人間によるその他の操作を漠然と長時間待機したりすることはありません。この操作は持続性を必要とせず、オール オア ナッシングの操作と見なされます。後にわかるように、典型的なトランザクション構成についての説明は、基本的に ACID トランザクションについての説明です。
では、長期トランザクションとはどのようなものでしょうか。それは長期にわたって続行し、要求に応じて中断したり、再開したりすることができる ACID トランザクションでしょうか。長期トランザクションは、トランザクションという用語をはるかに広義に解釈したトランザクションです。LRT は、ゆるやかに結合した独立システムを含む、1 つにまとまったアクションのセットです。
トランザクションによっては、長期にわたって実行される性質のために、ACID トランザクションとして管理するのは困難か、完全に非現実的な場合があります。ACID トランザクションでは、往々にして何らかの重要なデータをトランザクションの実行中にロックする必要があります。これは、数秒しかかからないトランザクションの場合は問題になりません。しかし、特定のビジネス トランザクションに含まれるリソースのいずれかを全プロセスの終了までロックしておくことができない場合、ACID パターンは適切ではありません。アクションと結果を統制するには、さまざまな操作を周辺のロジックと共に独立した操作として完了する必要があり、また必要に応じてエラーや障害を補正する必要があります。統制ロジックは、BMP サーバーとして機能する Windows Workflow Foundation ワークフローに組み込むか、最も外側のワークフローで使用される Windows Workflow Foundation 複合アクティビティで構築することができます。

ワークフローにおける ACID トランザクション
ACID トランザクションは、コミットまたはロールバックの決定が通常は数秒以内に行われる、短期のトランザクションです。ロールバック ロジックでは、実行中の操作と、その操作を構成するすべての中間手順をキャンセルできる必要があり、また前の操作の影響を補正する必要があります。ユーザーから見ると、何も起こらなかったかのように見えます。ACID トランザクションの完璧な例として、2 つ以上のテーブルに対してアトミックに機能するストアド プロシージャにコーディングされた典型的なデータベース トランザクションがあります。
ACID トランザクションは、ローカルにすることも、分散型にすることもできます。ローカル トランザクションは、接続しているデータベースなどの単一のリソースを含みます。分散トランザクションは、複数の異種リソースにまたがり、トランザクション処理モニタを必要とします。分散トランザクション コーディネータ (DTC) は、Microsoft Windows 2000 およびそれ以降にリリースされた Windows 用のトランザクション処理モニタです。
ワークフロー内にトランザクション セグメントを組み込むには、TransactionScope アクティビティを使用します (図 2)。トランザクションのスコープ内で作成されるすべてのアクティビティは、標準の ACID スキーマを実現する作業単位を形成します。すべての子アクティビティが正常に完了した場合、トランザクションはコミットされ、ワークフローは次へ進みます。いずれかの子タスクから例外がスローされると、TransactionScope アクティビティはロールバック操作を実行します。
図 2 Visual Studio 2005 の TransactionScope アクティビティ (画像を拡大するには、ここをクリックします)
図 2 に示すサンプル ワークフローは、注文番号を受け取り、支払いに進みます。支払金額がまず顧客の口座から引き出され、ベンダの口座に追加されます。この操作はアトミック、つまり両方の手順が正常に完了するか、両方とも失敗するかのどちらかである必要があります。いずれの場合も、関連リソースは、いかなる制約にも違反せず、かつデータの整合性が保証される、一貫した状態に保たれる必要があります。
内部の手順は、開発者とエンド ユーザーの双方にとって透過的です。状態が確定するまでは、どんな外部ソフトウェア コンポーネントも、トランザクションに関連するリソースの状態にアクセスしないことが理想的です (この制約は、パフォーマンス上の理由で、修正という相応な代償により緩和されることがあります)。したがって分離を実現するには、シリアル化可能な基盤トランザクションが必要です。これはデータベースおよび同様なリソース マネージャがサポートするトランザクションの中で、最も負荷の高いトランザクション タイプです。

TransactionScope アクティビティについて
Windows Workflow Foundation では、逐次的に実行され、ACID セマンティクスを完全に満たす子アクティビティのコンテナとして、TransactionScope アクティビティを使用します。トランザクション スコープ内で Prallel アクティビティを使用する場合、アクティビティはそのアクティビティ内では同時に実行されますが、スコープのその他の部分に対しては逐次的に実行されます。
TransactionScope アクティビティは、トランザクションを構成するために宣言的に使用できる 2 つのプロパティを公開します。それが IsolationLevel と TimeoutDuration です。IsolationLevel プロパティは、System.Transactions 名前空間で定義される列挙体である IsolationLevel 型です。Iトランザクションの分離レベルは、内部データに対して他のトランザクションが有するアクセスのレベルを、トランザクションが完了する前に決定します。図 3 に、最も一般的に使用されるレベルでの動作について詳しく示します。

レベル 説明
READ UNCOMMITTED 最も制約が緩いこのレベルは、他のトランザクションによって設定されたロックを無視します。他のトランザクションによってまだコミットされていない変更済みデータを読み取ることができます (ダーティ読み取り)。
READ COMMITTED SQL Server の既定の分離レベルであるこのレベルは、ダーティ読み取りを防止します。しかし、トランザクションが現在のトランザクション内でデータを変更、挿入、または削除することは許容します。これにより、反復不可能な読み取りやファントム行が生じることがあります。
REPEATABLE READ このレベルは、ダーティ読み取りを防ぎ、他のトランザクションが現在のトランザクションによって読み取られたデータを変更または削除できないようにします。新しいデータの挿入は可能です。
SERIALIZABLE 最も制約が厳しいこのレベルは、トランザクションが完了するまでロックを保持します。トランザクションが完了するまで、読み取りや更新を行うことはできません。
SNAPSHOT SQL Server 2005 のみで使用できるこのレベルは、トランザクション内で読み取られたデータが、他の同時トランザクションによって行われた変更を反映しないことを指定します。
CHAOS このレベルの場合、より高度な分離レベルのトランザクションが保留している変更を上書きすることはできません。
IsolationLevel の既定値は Serializable です。この値は、最も安全で、かつ最も負荷の高いオプションであり、すべてのケースで厳密に要求されるわけではありません。シリアル化可能なトランザクションは、トランザクションの継続中にデータをロックし、同時に実行されている他のトランザクションが同じデータにアクセスできないようにします。しかし、TransactionScope アクティビティは、30 秒のタイムアウトを設定することにより、このオプションを緩和します。同時実行に関する問題に不安があり、なおかつトランザクションを SQL Server™ 2005 で実行する必要がある場合は、新しい Snapshot 分離レベルを選択できます。他の分離レベルと異なり、Snapshot はプログラムによる設定のみに制限されているわけではありません。この分離レベルは、次のステートメントを使用して管理的な構成を行うことにより、SQL Server 2005 データベースに適用することができます。
ALTER DATABASE <name> SET ALLOW_SNAPSHOT_ISOLATION ON
TransactionScope アクティビティは、.NET Framework 2.0 の TransactionScope クラスのインスタンスを中心として構築されるラッパーです。TransactionScope クラスは、これまでに設計されたクラスの中で最もプログラマにとってフレンドリーなクラスの 1 つです。すべてのものを TransactionScope オブジェクトにラップすれば、作業はほぼ終了です。後はオブジェクトがすべてを引き受けて処理します。このオブジェクトは、ローカル トランザクションと分散トランザクションのどちらが必要かを判断し、必要な分散リソースを確保し、さもなければローカル処理に進みます。コードがローカルでは実行できないポイントに達すると、必要に応じて DTC にエスカレーションします。
ITransaction インターフェイスを実装するトランザクションに任意のオブジェクトを参加させることができます。これには、すべての標準 ADO.NET 2.0 データ プロバイダと、Microsoft メッセージ キュー (MSMQ) が含まれます。
何らかのコードが TransactionScope オブジェクトの Complete メソッドを呼び出すとき、それはトランザクションのスコープ内ですべての操作が適切に完了したことを意味します。このメソッドは分散トランザクションを物理的に終了しないことに注意してください。TransactionScope の破棄でコミット操作がまだ発生するためです。ただし、Complete メソッドを呼び出した後は、分散トランザクションを使用することはできなくなります。
TransactionScope アクティビティを使用すると、Visual Studio® 2005 デザイナでアクティビティをグループ化するだけで、簡単にトランザクション タスクを作成できます。図 2 に示すアクティビティのコードを考えてみてください。重要なコードを図 4 に示します。
private void WithdrawFunds_ExecuteCode(object sender, EventArgs e)
{
    Console.WriteLine(“Funds withdrawn to process order: #” + 
        orderNo.ToString());

    // Execute a SQL operation on ClientAccount
    SqlHelper.ExecuteNonQuery(conn,
        System.Data.CommandType.Text,
        “use bank update ClientAccount set Balance = Balance - 100”);
}

private void AddFunds_ToVendor_ExecuteCode(object sender, EventArgs e)
{
    Console.WriteLine(“Funds added to the vendor’s account for order #” + 
        orderNo.ToString());

    // Execute a SQL operation on VendorAccount
    SqlHelper.ExecuteNonQuery(conn,
        System.Data.CommandType.Text,
        “use bank update VendorAccount set Balance = Balance + 100”);
}
このトランザクション コードには、3 つの Code アクティビティが含まれています。最初の Code アクティビティは、データベース コマンドを実行し、指定された金額を差し引くことによってクライアントの口座の残高を更新します。次に、同じ金額をベンダの口座の残高に追加します。基になる TransactionScope オブジェクトの性質により、データベースを容易に分散させることができます。
さらに重要なのは、資金の転送はアトミック操作である必要があるということです。実行フローが TransactionScope アクティビティの末尾に達すると、トランザクションは適切に完了してコミットされます。いずれかのポイントで例外が発生すると、トランザクションはすべての作業を自動的にロールバックします。これらすべての処理は、ワークフロー開発者には完全に透過的です。ワークフロー開発者は、補正やエラー処理について考える必要はありません。
サンプル コードは、図 2 の最後のアクティビティのコードが示しているように、注文番号が奇数の場合に例外をスローします。
void CheckConsistency_ExecuteCode(object sender, EventArgs e)
{
    if (orderNo % 2 > 0)
        throw new DiscontinuedProductException();
}
スローされる例外は、アプリケーションの特定の状態を記述するカスタム例外オブジェクトです。これだけで、基になる .NET TransactionScope オブジェクトのロールバック メカニズムがトリガされます。図 5 に、実行中のアプリケーションと、ロールバックの場合に返されるメッセージを示します。図 6 に、コミットされた成功トランザクションを示します。
図 6 コミットされた送金 (画像を拡大するには、ここをクリックします)
図 5 キャンセルされた送金 (画像を拡大するには、ここをクリックします)
ワークフローの周辺、特に TransactionScope アクティビティの周辺で発生した例外をキャッチする必要がある場合は、エラー ハンドラ ビューに切り替え、FaultHandler アクティビティを追加します。これにより、特定の例外タイプをキャッチするアクティビティを構成し、そのスコープに、例外を処理するために必要なアクティビティをいくつでも追加することが可能になります。これは ACID シナリオではあまり意味がありませんが、一般に、例外の処理中に別のトランザクションを実行することができます。エラー ハンドラの目的は、例外が発生したアクティビティの部分的かつ失敗した処理を取り消すことです。ただし、トランザクション スコープに関する限り、ロールバックは自動的に行われ、アドホック SQL コマンドを記述して以前のデータベース操作の影響を取り消す必要はありません。

例外用のトランザクション ワークフローの構成
TransactionScope アクティビティの使用に関しては、注意する必要のある制約がいくつかあります。まず、Suspend アクティビティを使用して、トランザクション内からワークフローを中断することはできません。さらに、TransactionScope アクティビティを別の TransactionScope アクティビティや ICompensatableActivity インターフェイスを実装する任意のアクティビティ内に入れ子にすることはできません。ICompensatableActivity インターフェイスを実装するアクティビティの例として、ツールボックス内の CompensatableTransactionScope アクティビティや CompensatableSequence アクティビティがあります。
トランザクション ワークフローには永続化サービスが必要です。そのようなサービスが使用できなければ、例外がスローされます。永続化サービスは、クライアント アプリケーションのワークフロー ランタイムで次のようにして登録します。
string conn = “...”;
workflowRuntime.AddService(
    new SqlWorkflowPersistenceService(conn));
SqlWorkflowPersistenceService クラスは既定の永続化サービスであり、SQL Server データベースに基づいています。永続化は、ACID トランザクションが完了したとき、またはワークフロー インスタンスがアイドル状態になるかプログラムによってアンロードされたときに発生します。ワークフロー ランタイム エンジンは、永続化サービスのメソッドを呼び出して、ワークフロー インスタンスの状態を保存します。ワークフロー ランタイム エンジンは、永続化をいつ、どのようにして実行するかを判断します。永続化サービスは、選択されたデータ ストアとの間で、実際にワークフローの状態を保存したり読み込んだりする処理を行います。
SqlWorkflowPersistenceService クラスによって使用されるデータベースは、インストール時には作成されませんが、後で使用できるようにスクリプトがクライアント マシン上にコピーされます。スクリプトのパスは次のとおりです。
%WINDOWS%\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL
データベースの既定の名前は変更可能です。しかし、子テーブルの構造を変更することはできません。別のデータベース レイアウトを使用するには、カスタム永続化サービスが必要です。カスタム永続化サービスの中心となるのは、WorkflowPersistenceService を継承するクラスです。

Windows Workflow Foundation によるビジネス プロセスのモデル化
ACID トランザクションは、現実世界のワークフローのごく一部にすぎません。通常は、1 つのワークフロー内で複数の ACID トランザクションを組み合わせて、特定のビジネス プロセスを表現します。そのため、あるトランザクション アクティビティが適切に完了しているのに、後で、同じワークフロー内の別なアクティビティで例外がスローされることがあります。この場合、自動ロールバックを行うことはできません。ロールバックはトランザクションのコンテキストで可能です。操作を追跡し、必要に応じてキャンセルするリソース マネージャの存在が背後にあるためです。しかし、ワークフローはトランザクション セマンティクスを完全にはサポートせず、トランザクション アクティビティをサポートするだけです。
Windows Workflow Foundation を使用してビジネス プロセスをモデル化する際、ワークフロー全体にさまざまなトランザクション ブロックを散りばめることがあります。Windows Workflow Foundation では、2 種類のトランザクション アクティビティを使用できます。それが ACID トランザクションと補正可能トランザクションです。ACID トランザクションは、TransactionScope アクティビティによって完全に表現されます。ACID トランザクションは、コミットされるかロールバックされるかのいずれかであり、結果を永続化します。しかし、データを長時間にわたってロックすることができず、過去のコミットをロールバックできるようにする必要がある大きなプロセスのコンテキストで、トランザクション タスクを実装する必要がある場合は、どうすればよいでしょうか。この場合は、CompensatableTransactionScope アクティビティという特殊な種類のトランザクション アクティビティが必要です。
CompensatableTransactionScope は補正メカニズムをサポートします。補正とは、以前の操作の影響を取り消したり、緩和したり、補正したりするために、何らかの時点で実行するロジックです。重要なのは、補正可能トランザクションには、いったんコミットされたらロールバックすることができない子 ACID トランザクションが含まれている可能性があるということです。しかし、以降にエラーが発生した場合は、それらのトランザクションの影響を何らかの方法で補正する必要があります。補正はロールバックに似ていますが、実行された作業を補正するために使用されるコードを開発者が記述する必要があります。すべてのトランザクション タスクを補正可能にする必要はありません。たとえば、ACID トランザクションで効果的に表現できるタスクには、補正は不要です。実際、TransactionScope アクティビティは補正をサポートしていません。
図 7 に、2 つの補正可能トランザクション スコープを使用するサンプル ワークフローを示します。このビジネス プロセスは、注文のライフサイクルをモデル化しています。注文が行われると、顧客のクレジット カードへの課金が行われ (Scope_ChargeCreditCard という名前のアクティビティ)、お金がベンダの銀行口座に送金されます (Scope_PayOrder という名前のアクティビティ)。どちらのアクティビティもトランザクションと補正に対応しています。たとえば、注文された商品が入手できなくなったために、ワークフロー内で後に例外が発生したとします。この時点で、ビジネス例外が発生し、それまでに行われた作業をすべて取り消す必要が生じます。コミットされたすべての ACID トランザクションと、トランザクションに対応していない一連のアクティビティの影響を補正する必要があります。それぞれの補正可能トランザクションまたは連続アクティビティには、作業のすべてまたは一部を取り消すことだけを目的とするロジックが含まれています。
図 7 補正可能トランザクション 
Windows Workflow Foundation では、アクティビティのビューを補正ハンドラ ビューに切り替えることにより、各補正可能アクティビティの取り消しコードを定義します (図 8)。補正ハンドラ ビューでは、操作の影響を取り消すために実行する必要があるすべてのアクティビティを一覧表示します。
図 8 ワークフロー シーケンスへの 補正ロジックの追加 (画像を拡大するには、ここをクリックします)
補正の概念は、ロールバックの概念に似ています。しかし、ロールバックが純粋にトランザクション操作であるのに対し、補正はトランザクション操作と非トランザクション操作、どちらの一連の操作にも適用されます。図 7 と図 8 を比較すると、2 つの補正可能トランザクション スコープには、若干のユーザー インターフェイスの違いがあることがわかります。図 8 には、直接コードの代わりに補正コードがあります。

補正について
ここでの疑問は、なぜわざわざ補正を行うのかということです。自動ロールバックを備えた大規模な ACID トランザクションで十分なのではないでしょうか。ACID トランザクションが最も適しているのは、同じデータベース内または同じ情報システム内で操作が発生する場合です。また、ACID トランザクションは、操作がすばやく終了する場合にほぼ適しています。異なる企業やサービスが含まれる場合、ACID セマンティクスの観点でプロセスを定義するのは、往々にして困難です。そのようなプロセスを独立的で永続的なものにするには、タスクが継続している間、さまざまな企業のすべてのリソースをロックし続ける必要があります。これは、特にタスクが長期である場合、かなり非現実的です。プロセスを一貫したアトミックなものにするには、アドホック補正コードが必要です。
もう 1 つの疑問は、何によって補正コードを起動するかということです。エラー処理では、ワークフローによって発生した 1 つ以上の例外を処理することになります。そのような例外のハンドラは、Compensate アクティビティという名前の、もう 1 つの特別なアクティビティです (図 9)。Compensate アクティビティは、ビジネス例外によってリスクを伴うようになった制約およびビジネス ルールを修正するコードを起動します。Compensate アクティビティは、ワークフロー内の補正可能アクティビティにバインドする必要があります。それには、Visual Studio 2005 Workflow Extensions デザイナで TargetActivityName プロパティを使用します。TargetActivityName プロパティを特定のトランザクションまたはシーケンスの名前に設定すると、そのトランザクションまたはシーケンスだけが補正されます。複数の Compensate アクティビティを追加して、補正が発生する順序と精度を決めることができます。従来的な ACID トランザクションのロールバック ロジックを模した補正ロジックを実行するだけの場合は、Compensate アクティビティをワークフロー全体にバインドします。この場合、補正は例外が発生したポイントからワークフローのルートに向かって、ボトムアップ式に進行します。
図 9 Compensate アクティビティ 
ワークフローの実行結果のバランスをとるために、追加の ACID トランザクションが補正コードによって開始されることがあります。さらに、補正が失敗することもあります。したがって、安全を期すためには、補正コード用の適切な例外ハンドラも用意する必要があります。

パフォーマンスとワークフロー トランザクション
Windows Workflow Foundation ワークフローのパフォーマンスに最も大きな影響を与える要素の 1 つは、永続性です。ワークフローは、複数の永続ポイントを持つことができます。プログラマによって明示的に定義されるものや、組み込みのアクティビティとカスタム アクティビティによって暗黙的に要求されるものがあります。永続化サービスは、ワークフローがアイドル状態になったとき、ワークフロー インスタンスで Unload メソッドが呼び出されたとき、または PersistOnClose 属性で修飾されたアクティビティが作業を完了したときに、自動的に呼び出されます。Windows Workflow Foundation で定義されている組み込みのトランザクション アクティビティ (TransactionScope および CompensatableTransactionScope) は、いずれも終了時に永続化を必要とします。したがって、効果的な永続化サービスと、それ以上に、そのための効果的なランタイム環境が、パフォーマンスにとって重要です。
トランザクション ワークフローでは、ランタイム マシンに関して 2 つの要件があります。それは、永続化とトランザクションのサポートです。既定では、.NET TransactionScope クラスを介してトランザクションを管理するために、DefaultWorkflowCommitWorkBatchService サービス クラスが使用されます。この方法では、必要な場合にのみ、DTC への動的なエスカレーションが暗黙的に提供されます。しかし、パフォーマンス上の理由により、既定のサービスの使用を避けるのが望ましい特定のシナリオがあります。
既製の永続化サービスを使用するときは、適切なデータベース (SQL Server 2005 または SQL Server 2000) を作成する必要があります。標準のデータベースには、標準の追跡サービスである SqlTrackingService クラス用のテーブルとストアド プロシージャも含まれています。両方のサービスが有効になっており、同じデータベースを使用する、つまりまったく同じ接続文字列を使用するシナリオを考えてみてください。データの永続化と追跡は、常に同じトランザクション内に記述されます。しかし、データベースが SQL Server 2000 の場合は、これらのサービスが同じ接続オブジェクトを共有するようにしない限り、DTC へのエスカレーションが発生します。したがって、データの永続化と追跡を同じ SQL Server 2000 データベースに対して実行するときは、既定のサービスではなく、SharedConnectionWorkflowCommitWorkBatchService を使用する必要があります。これらのサービスをワークフロー ランタイムに追加するには、次のようにします。
workflowRuntime.AddService(
    new SqlWorkflowPersistenceService(connString)); 
workflowRuntime.AddService(
    new SqlTrackingService(connString));
workflowRuntime.AddService(
    new SharedConnectionWorkflowCommitWorkBatchService(connString));
SharedConnectionWorkflowCommitWorkBatchService サービスは、この特定のシナリオに限って、ワークフローのパフォーマンスを最適化します。これにより、データベースおよび DTC トランザクションに追加の接続を行うオーバーヘッドが回避されます。トランザクション ワークフローで追跡が不要な場合や、追跡と永続化に個別のデータベースを使用する場合は、トランザクション サポートの既定サービスを利用するほうが無難です。
補足になりますが、追跡サービスと永続化サービスは、常に同じトランザクション内で動作します。追跡サービスには、IsTransactional という名前のブール型プロパティがあります。このプロパティを false に設定しても、追跡サービスがトランザクションに対応しなくなるわけではありません。より端的に言うと、追跡しているデータは、対応するメソッドが呼び出されたときに保存されるようになります。IsTransactional が true の場合 (既定の設定)、サービスの TrackData メソッドは、データを作業バッチに追加します。作業バッチは、ワークフロー内の次の永続ポイントでフラッシュされます。

まとめ
Windows Workflow Foundation でのトランザクション タスクのモデル化は、実際には非常に簡単です。応答をすばやく生成する ACID トランザクション (アトミックで、一貫していて、独立していて、永続的なトランザクション) が必要な場合は、TransactionScope アクティビティを使用することをお勧めします。このアクティビティ内に、基になるアンビエント トランザクションに参加するオブジェクトにアクセスする、他のアクティビティを作成します。一方、多数の疎結合サービスを統制して完了に時間のかかるタスクをモデル化する必要がある場合は、CompensatableTransactionScope を使用することをお勧めします。
また、トランザクションは永続化とメモリの必要量という要件により、固有の代償をもたらすということを覚えておく必要があります。補正のみが必要な場合は、軽量な CompensatableSequence アクティビティを選択することをお勧めします。

ご意見やご感想は、

Dino cutting@microsoft.com.

まで英語でお送りください。
Dino Espositoは、Solid Quality Learning の指導者であり、『プログラミング Microsoft ASP.NET 2.0』 (日経 BP ソフトプレス社発行、2006 年) の著者でもあります。Dino はイタリアに在住し、世界各地で開催される業界のイベントで頻繁に講演しています。cutting@microsoft.com (英語) で連絡を取るか、weblogs.asp.net/despos (英語) のブログに参加してください。

Page view tracker