May 2010

Volume 25 Number 05

ワークフロー サービス - WCF と WF 4 によるワークフローのビジュアル デザイン

Leon Welicki | May 2010

コード サンプルのダウンロード

分散アプリケーションを作成する手段として、サービス指向アーキテクチャ (SOA) が採用されることが増えてきていますが、初心者にとっては、サービス指向型の分散アプリケーションをデザインし、実装することは手を出しにくいと感じられるかもしれません。しかし、Microsoft .NET Framework 4 では、Windows Workflow Foundation (WF) を使用して Windows Communication Foundation (WCF) サービスをかつてないほど簡単に実装できます。

WCF ワークフロー サービスは、処理期間が長い持続性が必要な操作やサービスを効率よく作成できる環境を提供します。このような操作やサービスでは、操作に順序を付けることでアプリケーションの手順を取り決めることが重要です。ワークフロー サービスは、データの送受信に WCF を利用できる WF アクティビティを使用して実装されます。

今回のコラムでは、WCF と WF の機能のうち .NET Framework 4 で導入された機能をいくつか組み合わせて、ある不動産会社で使用される、処理期間が長く、持続性が必要なインストルメント化された住宅ローン承認プロセスをモデル化する方法について説明します。今回は、WCF または WF の概要説明や、稼働するソリューションを作成する全行程を説明することが目的ではありません。ここでは、実践的なビジネス シナリオを使って、.NET Framework 4 の重要な新機能を使用する方法を説明します。稼働する完全なソリューションは、ダウンロード コードに含めています。

シナリオ

まず、このコラムのワークフロー アプリケーションを作成する基盤となるシナリオについて簡単に説明します。Contoso Housing は、戸建てやマンションを販売している不動産会社です。顧客へのサービスを向上し、包括的な取り引きを提供できるように、Contoso は見込み顧客に必要な住宅ローンを手配する住宅金融会社 3 社と提携しています。住宅金融会社ごとに、設定利率が異なります。Contoso では、(利率がよいほど物件の販売確率が高まるという前提で) 利率を基に住宅金融会社の優先順位を付けて、顧客が最も有利なローンを組めるようにしています。

顧客は、住宅ローンの申し込みデータを Web アプリケーションを通じて提出します。顧客は、顧客 ID、物件価格、頭金額、住宅ローン年数、収入情報、属性情報を入力します。

アプリケーション ワークフローの最初の手順 (図 1 参照) では、顧客が入力したデータを使用して、ローン申し込みを住宅金融会社に送信する前に、顧客を審査して適格かどうかを判断します。

住宅ローン承認プロセス
図 1 住宅ローン承認プロセス

ローン申請は、次のルールに従います。

  • 申請者に差し押さえや破産歴があるか、係争中であると、申請は却下されます。
  • 申請者に信用履歴がなく、頭金が 2 割未満であれば、申し込みは差し戻され、見直しが求められますが (不適切な申請)、却下されることはありません。この顧客が申請処理を続けるには、2 割以上の頭金を用意しなければなりません。
  • 上記のいずれにも該当しなければ、申請が受け付けられます。

住宅金融会社へは、Contoso が設定した融資申請の優先順位に従って連絡されます。ある住宅金融会社に却下されると、次の住宅金融会社に問い合わせが行われます。住宅ローン承認申請について、すべての住宅金融会社が実装している標準のサービス コントラクトがあります。申請プロセスのこの段階で、顧客は申請状態を照会できます。

融資申請の最終結果が出る (住宅金融会社のうちの 1 社が承認するか、すべての会社が却下する) と、Contoso の顧客関係管理 (CRM) システムが公開するサービスによって、顧客の申請履歴が CRM システムに記録されます。申請結果は、顧客に通知されます。

図 1 の住宅ローン承認プロセスはビジネス プロセスの概要を示していますが、プロセスをどのように実装するかについては何も示されていません。ワークフロー サービスが、このプロセスの実装になります。

宣言型サービスを作成する

ワークフロー サービスでは、顧客からデータを受け取り、最初の審査を実行して、住宅金融会社との交渉を調整し、CRM サービスに申請履歴を登録して、結果を顧客に通知します。

このサービスは、処理期間が長く (完了までに数日または数か月かかる可能性がある)、持続性を要し (状態を保存し、後で再開できる)、インストルメント化された (開発者とユーザーの両方が、サービスをデバッグしなくても処理状況を確認できる) サービスになります。WCF と WF を使用することで、コードを記述することなく、これをすべて宣言で実現できます。必要な作業は、.NET Framework が提供するコンポーネントを組み合わせて構成することだけです。図 2 は、このソリューションのアーキテクチャのダイアグラムです。

ソリューションのアーキテクチャ
図 2 ソリューションのアーキテクチャ

WCF ワークフロー サービスは、ワークフローを定義する .xamlx ファイルに含まれています。ビジネス プロセスは WF デザイナーを使用して視覚的に定義できます。サービス コントラクトは、ワークフローの構造を基に推測されます。

Visual Studio 2010 を使用してワークフロー サービスを作成するには、新しいプロジェクトを作成し、次に WCF ワークフロー サービス アプリケーション テンプレートを選択します。このプロジェクト テンプレートでは、Receive アクティビティと SendReply アクティビティを含む、非常に簡単な (しかし稼働する) ワークフローが作成されます。これは、整数を受け取って文字列を返すメソッドを含むクラスに似ています。このワークフローは、他のアクティビティを追加することで拡張できます。追加するアクティビティによって、実行ロジックやサービス操作が追加される場合があります。

サービス コントラクトを定義するには、WF が提供するメッセージング アクティビティを使用します。サービスの構成は、通常の WCF サービスと同様に、web.config ファイルに保存されます。WCF ワークフロー サービスは、WCF 4 で導入されたサービス構成に関する機能強化を利用しているため、web.config ファイルを開くと、非常に簡潔な構成ファイルであることがわかります。

メッセージング アクティビティは、WF と WCF をシームレスに組み合わせます。メッセージング アクティビティは、メッセージ指向のワークフローをサポートするためのアクティビティで、よりスムーズにメッセージングをワークフローに統合できます。メッセージング アクティビティを使用すると、ワークフローから他のシステムにデータを送信したり (Send、SendReply、および SendAndReceiveReply)、他のシステムからデータを受信したり (Receive、ReceiveReply、および ReceiveAndSendReply) できます。また、メッセージング アクティビティには、関連付け (InitializeCorrelation、CorrelationScope) やトランザクション (TransactedReceiveScope) を処理するためのアクティビティもあります。

Receive アクティビティと Send アクティビティを使用すると、ワークフロー内にメッセージ通信をモデル化できます。サービスに含まれるコントラクトは、コントラクト内に Receive アクティビティと Send アクティビティを構成することで定義できます。各 Receive アクティビティは、操作として公開されます。各 Send アクティビティは、メッセージをサービスに送信します。目的のサービスでは、標準のプロトコルを利用して通信を行うため、WCF や .NET Framework さえも使用する必要がありません。

Send アクティビティと Receive アクティビティは、実際のデータ送受信に、メッセージ ベースかパラメーター ベース (RPC) のどちらかの方法を使用するように構成できます。これにより、データの送受信に使用するワイヤ形式を制御できます。

プロセスをフローチャートで記述する

プロセスはシーケンスを使用してモデル化できますが、図 1 を見ると、このプロセスではある時点で前の手順にループ バックする必要があります。このような動作は、シーケンシャル ワークフローでは直接サポートされません (十分に考えてモデル化された条件を設定して、While アクティビティなどのコンストラクトを使用して、手動でループをモデル化する必要があります)。このシナリオのモデル化には、フローチャートの方が適しています。

フローチャートは、WF 4 で導入された新しい制御フロー アクティビティで、ホワイトボード上にプロセスを書くのと同じように、プロセスを記述できます。フローチャートでは、実行経路が 1 つで、特定の状況では前の手順へのループ バックが必要な場合があるシーケンシャルな性質のプロセスを記述します。フローチャートは、さまざまな方法の中でよく使用される方法の 1 つで、矢印と四角形を使用してプロセスを記述します。

WCF ワークフロー サービス プロジェクト テンプレートによって作成される既定のワークフローは、シーケンスですが、ワークフロー サービスで使用できる制御フロー アクティビティは他にもあります。フローチャート (またはその他の複合アクティビティ) をサービスのルート アクティビティとして使用するには、単純にシーケンスを削除してフローチャートを追加します。

図 3 は、ビジネス ユーザーが通常プロセスを記述するために作成するダイアグラムから、いかに簡単にフローチャートを作成できるかを示しています。

フローチャートで表した住宅ローン承認プロセス
図 3 フローチャートで表した住宅ローン承認プロセス

フローチャートは、ビジネス プロセスの記述に非常に便利なツールで、実行可能なプロセスとプロセスを指定する方法との間のずれを最小限に抑えることができます。この場合、フローチャートの定義は、プロセスのドキュメントになります。

しかし、少しペースを落としましょう。空のフローチャートからすぐに完成したフローチャートに話を進めてしまいました。空のフローチャートに戻り、そこから作業を始めましょう。

住宅購入の見込み顧客は、Web アプリケーションを通じて自分のデータを提出します。このデータは、Receive アクティビティ経由でワークフローに渡されます。Receive アクティビティをフローチャートに含めると、WorkflowServiceHost によってエンドポイントが公開され、ユーザーはこのエンドポイントにメッセージを送信することでワークフローと通信できます。

前述のとおり、Send アクティビティと Receive アクティビティはメッセージ ベースか RPC ベースの方法を使用するように構成できます。ここでは、RPC ベースの方法を使用するように Receive アクティビティを構成しました。つまり、メソッド呼び出しと同様に、Receive アクティビティは一連の入力引数を受け取ります。一連の入力引数をワークフローに提供できるようにするには、変数を作成し、それらの変数をパラメーターにバインドする必要があります (図 4 参照)。他に考えられる方法は、これらのフィールドがすべて折りたたまれている DataContract を用意することです。

入力パラメーターの構成
図 4 入力パラメーターの構成

ワークフローの新しいインスタンスを作成して開始するには、Receive アクティビティの CanCreateInstance プロパティを true に設定する必要があります。つまり、WorkflowServiceHost がこのエンドポイントからメッセージを受信すると、新しいインスタンスを作成します。

WF コンポジションを使用して審査手順をモデル化する

ワークフローにデータが渡されたら、データの処理を開始できます。最初の手順は審査です。つまり、住宅金融会社に連絡する前に、申請者が住宅ローンを受ける資格があるかどうかを判断するための一連の条件を検証します。

方法の 1 つとしては、本体のフローチャートにいくつかの決定図形 (FlowDecision) を追加します。これは機能しますが、プロセス全体が読みにくくなります。また、審査ルールを変更すると、本体のフローを更新しなくてはなりません。視覚的に条件を表現するのにフローチャートが適しているように思えますが、本体のプロセスは簡潔にしておきます。

ソリューションの 1 つは、既存のフローチャート内に新しいフローチャートを追加することです。WF 4 では、基本機能としてコンポジションを強力にサポートしているため、アクティビティを自由に構成できます。つまり、新しいフローチャートは必要なときにいつでも、既存のフローチャート内であっても、追加できます。また、コンポジションは任意であり、何の制限もありません。既存のアクティビティを自由に組み合わせることができます。

子のフローチャートは、親のフローチャート内で折りたたまれて表示されます (フローチャート デザイナーは、配置先での展開をサポートしていません)。子のフローチャートをダブルクリックして、審査ロジックをモデル化する必要があります。審査のフローチャート (図 5 参照) は、本体のフローチャートの子で、本体のフローチャートの変数と引数を利用できます。

審査のフローチャートの追加
図 5 審査のフローチャートの追加

コードを作成する場合はどうなるでしょう。もちろん、審査プロセスを記述するコードは作成できます。この場合は、たとえば、顧客から入力としてデータを受け取り、検証 (任意の言語で表した、一連の if ステートメントのチェーン) を実行して、結果を返す CodeActivity を作成できます。これには特有の長所と短所があります。具体的には、コードの方が、宣言型の方法よりもパフォーマンスが向上し、より簡潔な表現ができる可能性があります。一方、プロセスを視覚的に表現できなくなる (不透明化) ほか、プロセスを変更するにはコードの変更と再コンパイルが必要になります。

結果を顧客に送信する

審査の検証が完了したら、結果を申請者に返す必要があります。ワークフローの冒頭で Receive アクティビティを使用して、該当するデータを受け取りました。応答を送るには、SendReply アクティビティを使用します (既存の Receive に対する SendReply は、Receive を右クリックし、[SendReply の作成] をクリックして作成できます)。Receive と SendReply の組によって、要求 - 応答のメッセージ交換パターンを実装できます。SendReply は、操作の結果と説明メッセージをクライアントに返すように構成します。

Send ではなく、SendReply を使用するのはなぜでしょう。Receive アクティビティと Send アクティビティの組を使用して "要求して応答を待つ" ような二重メッセージ交換パターン (コールバックに類似) のモデル化はできますが、要求 - 応答メッセージ交換パターン (メソッド呼び出しに類似) のモデル化には、SendReply の方が適しています。

次の処理を決定する

審査が完了したら、ワークフローは、住宅ローン申請プロセスを続行できます。この時点では、却下と承認の 2 つの分岐がありますが、申請者がさらにデータを提供する必要がある場合、ワークフローは前の手順に戻る必要があります。フローチャートでは、このようにワークフローの次の処理となる手順に "線を引く" アクションのモデル化が可能です。

ここでは、審査の結果を基にパスを決定するために、FlowSwitch アクティビティ (switch ステートメントに類似) を使用します (図 6 参照)。

FlowSwitch から出る各矢印は、switch の case を表現
図 6 FlowSwitch から出る各矢印は、switch の case を表現

関連付け

住宅ローンの申し込みが不適切であると判断された場合、ワークフローは、既存のワークフロー インスタンスに追加のデータを提供するように顧客に求めます。Receive アクティビティでは、受信中のデータが、以前に顧客が提供したデータの改訂版であることをどのようにして認識するのでしょう。つまり、ワークフローの実行中インスタンスに別のメッセージを送信するにはどうしたらよいでしょう。この答えは関連付けです。

WF 4 では、関連付け用のワークフローが導入されています。関連付けは、実際には次の 2 つのいずれかです。

  • メッセージをグループにまとめる方法。従来から使われているこの例には、WCF のセッションや、さらに簡単なところでは要求メッセージとその応答間の関係などがあります。
  • データをワークフロー サービスのインスタンスにマップする方法。

.NET Framework 4 には、何種類かの関連付けが用意されています。このサンプル ワークフローでは、コンテンツ ベースの関連付けを使用します。コンテンツ ベースの関連付けでは、受信メッセージからデータを取得し、これを既存のインスタンスにマップします。コンテンツ ベースの関連付けは、1 クライアントからアクセスされる複数のメソッドがワークフロー サービスにあり、交換されるメッセージに含まれるデータによって目的のインスタンスが特定される場合に使用します。

関連付けを設定するため、ここでは CorrelationHandle 型の変数を宣言します。これは、関連付け情報の格納に使用されるハンドルです (付属のソリューションでは customerCorrelationHandle)。次に、この関連付けハンドルを使用します。Receive アクティビティのプロパティ グリッドには、関連付けを構成するための Correlations セクションがあります。このセクションには、関連付けハンドルを構成するプロパティ (CorrelatesWith)、関連付けクエリを使用した関連付けの基準となるデータを指定するプロパティ (CorrelatesOn)、および関連付けハンドラーを初期化するプロパティ (CorrelationInitializers) があります。ここでは、Receive アクティビティの CorrelatesWith および CorrelatesOn の引数を構成して、customerCode フィールドを基に関連付けを行うようにしました。

CorrelatesOn と CorrelatesWith は紛らわしい場合があります。区別を容易にする考え方としては、Receive CorrelatesWith は既存の関連付けハンドルで、CorrelatesOn のデータは関連付けクエリによって指定されます。

これはすべて WF デザイナーを使用して設定できます。

ここでは、顧客 ID を基準に関連付けを行うため、メッセージから customerCode を抽出する関連付けクエリを作成します。関連付けクエリは XPath を使用して作成しますが、受信したメッセージのコントラクトが WF デザイナーによって調べられて自動的にクエリが作成されるため、XPath の知識は必要ありません。

ワークフローの最初の Receive アクティビティは、メッセージを受信したら新しいワークフロー インスタンスを作成するように構成します。ただし、ここでは customerCode に基く関連付けも構成するため、ループバック時には新しいインスタンスは作成されません。最初の Receive アクティビティでは、新しいインスタンスを作成する前に、この関連付けキーを使用して既存のインスタンスの有無を確認します。

住宅金融会社に利率を問い合わせる

ローン申請が審査に合格すると、申請が査定のために住宅金融会社に送られます。前述のとおり、Contoso は 3 社の住宅金融会社と提携していて、取り引きにあたっての優先順位を設けています。したがって、このプロセスでは、いずれかの住宅金融会社が承認するまで、順番に住宅金融会社に問い合わせが実行されます。

すべての住宅金融会社は、住宅ローン承認の申し込み用に標準のサービス コントラクトを実装しています。住宅金融会社 3 社に対する利率の問い合わせの際に唯一違う点は、サービスの URI です。ただし、住宅金融会社への利率問い合わせは、メッセージを送信して応答を非同期に待機しながら、顧客からの状態問い合わせにも応答する必要があるため複雑です。

このプロセスをモデル化して、これをワークフローに 3 回コピーすることもできますが、それではかなりの量の不要な重複が発生し、その結果として重大なメンテナンスの問題が発生します。そこで、この手順を抽象化して、同じプロセスを複数回使用できるようにしたいと思います。理想的には、ワークフローになんらかの入力データを渡して、処理が完了したら結果を取得します。このような場合は、まさにカスタム アクティビティの出番です。

WF 4 では、2 とおりの方法でカスタム アクティビティを作成できます。

  • 宣言型: 他の既存のアクティビティを組み立てることで、新しいアクティビティを作成します。
  • 命令型: コードを記述することで、新しいアクティビティを作成します。CodeActivity (簡単な命令型の動作)、AsyncCodeActivity (非同期に処理を実行)、NativeActivity (WF ランタイムと対話) など、派生元として使用できる、機能の異なる基本クラスがいくつかあります。

アクティビティには、受信できるデータの種類 (InArgument) や処理の完了時に返すデータの種類 (OutArgument) についてのパブリック シグネチャを定義する引数があります。今回のアクティビティでは、入力として顧客の住宅ローン情報とサービスの URI を受け取り、利率と結果の文字列メッセージを返します。

ここでは、WF デザイナーを使用して、新しいアクティビティを Visual Studio の Activity 項目テンプレートを使用して宣言的に作成します。WF デザイナーでは、既存のアクティビティをドラッグ アンド ドロップして、引数を設定することで、視覚的にカスタム アクティビティを作成できます。WF デザイナーを使用してカスタム アクティビティを作成すると、x:Class を定義する XAML ファイルが作成されます。

このアクティビティの名前を AskVendor としました。AskVendor アクティビティは顧客の住宅ローン情報とサービスの URI を入力として受け取り、利率と結果の文字列メッセージを出力として提供します。利率が 0 の場合は、申請が却下されています。

AskVendor は、メッセージを住宅ローン住宅金融会社に送信して利率を問い合わせ、住宅金融会社からの応答を待機します。応答の受け取りまでは、数分や数時間で済むことも、数日間もかかる場合もあります。応答を待機している間に、申請者が処理の状態を確認できるようにします。したがって、このアクティビティは、申請者からの状態要求メッセージにも応答します。

両方の操作を同時に処理するため、ここでは、カスタム アクティビティのルートとして Parallel アクティビティを使用します。片方の分岐には住宅金融会社との通信用のアクティビティをすべて設定し、もう一方の分岐には、住宅金融会社からの応答の待機中に顧客からの要求をリッスンするアクティビティを設定します。AskVendor 内で使用されるすべてのメッセージング アクティビティは、customerCode フィールドを基に関連付けられるように構成します。図 7 に AskVendor カスタム アクティビティを示します。

AskVendor カスタム アクティビティ
図 7 AskVendor カスタム アクティビティ

前述のとおり、カスタム アクティビティのルートは Parallel です。左の分岐では、メッセージを住宅金融会社に送信し、応答を待機します。応答を受け取ると、結果の文字列メッセージの書式を整え、completed フラグを true に設定します。右の分岐では、申請者からの状態問い合わせ要求をリッスンします。Receive と SendReply は、completed フラグが true になるまで実行される While アクティビティ内にあります。Parallel アクティビティの完了条件 (分岐の完了時に実行される) によって、completed フラグが true に設定されます。したがって、左の分岐が完了 (住宅金融会社からメッセージを受信) すると、completed 変数が true になり、右側の While も完了になります。

カスタム アクティビティは、他のアクティビティとまったく同様です。カスタム アクティビティを作成すると、作成したカスタム アクティビティがアクティビティ ツールボックスに自動的に表示されます。また、カスタム アクティビティの使い方と、他の既存のアクティビティの使い方に違いはありません。つまり、ツールボックスからドラッグして、WF デザイナーにドラッグし、引数を構成します。ここでは、このアクティビティ用のデザイナーを作成していないため、このアクティビティには既定のデザイナーが割り当てられています (DisplayName プロパティを設定できる四角形)。プロパティ グリッドには、アクティビティのすべての引数が自動的に表示されます。

前に、WF 4 ではコンポジションの強力なサポートが提供されていることをお伝えしました。これは、カスタム アクティビティにも当てはまります。独自の複合アクティビティを作成する場合、Sequence、Flowchart、Parallel や、さらには他のカスタム アクティビティなど、他の既存のアクティビティを使用して自由に構成できます。

CRM サービスを呼び出す

Contoso の CRM システムは、コア機能をサービスとして公開します。このようなサービスの 1 つを利用して、顧客の申請履歴を登録できます。Send アクティビティを使用してこのサービスを呼び出すこともできますが、その場合、アクティビティを手動で構成し、サービス データ コントラクトをインポートしなくてはなりません。

単純に WF にサービスをインポートして実行できるとしたら、とても便利です。これこそが、ワークフロー サービス プロジェクトの "サービス参照の追加" の機能です。サービスのコントラクトが渡されると、サービスの呼び出しに使用できるプロキシ アクティビティを (サービス内の操作ごとに 1 つ) 自動的に作成します (図 8 参照)。このサンプルでは、CRM サービス コントラクトに 3 つの操作があるため、"サービス参照の追加" によって 3 つのアクティビティが作成され、これらはツールボックスに表示されます。

CRM サービスへのサービス参照の追加
図 8 CRM サービスへのサービス参照の追加

結果を伝える

最後に、結果を顧客に提示する必要があります。アプリケーションを簡潔に保つため、ここでは単純に、顧客に結果を公開する ReceiveAndSendReply アクティビティを使用します。顧客が結果を確認すると、ワークフローが完了します。

それには、フローチャートに ReceiveAndSendReply をドロップします。アクティビティをデザイナー画面にドロップすると、折りたたまれたシーケンスが追加されます。これは ReceiveAndSendReply がアクティビティ テンプレート、つまり、事前に構成されたアクティビティのセット (この場合は、Receive と SendReply が構成されたシーケンス) であるためです。これは、前に審査用に子フローチャートを追加したときに説明しました。

ReceiveAndSendReply を構成するには、Receive アクティビティにアクセスして、エンドポイント情報を設定する必要があります。また、顧客 ID を基に関連付けを行うように Receive を構成して、顧客が customerCode を使用してメッセージを送信したときに応答を受け取れるようにすることも必要です。

処理期間が長い作業

ワークフローが承認要求を住宅金融会社に送信すると、サービス インスタンスはアイドル状態になり、応答を待機します。応答は数分で返されることも、数時間、数日、または数週間後に返される可能性もあります。このために、興味深い課題が発生します。おそらく、すべてのインスタンスをメモリに保持することは、不要なシステム リソースを消費し、拡張性が失われるため、避ける方がよいでしょう。また、ホスト プロセスがクラッシュした場合 (または、これほど深刻ではなく、メンテナンスのためにシャットダウンが必要な場合)、完了していないインスタンスが失われることになります。

インスタンスで何の処理も行われていないときに、永続ストレージに保存してメモリから削除できたらとても便利です。これは、ワークフロー インスタンスをストレージ メディアに保存して後から取得できる、WF の永続化フレームワークを利用することで実現できます。

つまり、インスタンスは既存のプロセスやコンピューターに結び付けられません。サンプル ワークフローでは、ワークフロー インスタンスの実行やデータに影響を与えることなく、あるプロセスで審査を実行し、別のプロセスで最初の住宅金融会社に対して利率の問い合わせを行い、また別のプロセスで応答を受け取ることが可能です。これにより、リソースを効率よく利用でき、スケーラビリティが向上し、復元性が得られます。ホストがクラッシュしても、最後に保存された時点から処理を再開できるため、アクティブなインスタンスが失われることはありません。

ワークフロー インスタンスは、インスタンス ストアに保存されます。WF 4 には、SQL Server ベースのインスタンス ストアが付属しています。また、永続化フレームワークは拡張可能なため、独自のストアを作成できます (たとえば、SDK の PurchaseProcess サンプルでは、非常に簡単なテキスト ファイルのインスタンス ストアの作成方法を確認できます)。

ここでは組み込みの SQL Server インスタンス ストアを使用して、Contoso ワークフローのインスタンスを保持します。さいわいなことに、これを使用するためにコードを記述する必要はありません。ワークフロー サービスでは、永続化は次のように web.config ファイル内に構成できるビヘイビアの 1 つです。

<!--Set up SQL Instance Store-->
<sqlWorkflowInstanceStore connectionString="Data Source=.\SQLExpress;Initial Catalog=InstanceStore;
Integrated Security=True;Asynchronous Processing=True"/>
          
<!--Set the TimeToUnload to 0 to force the WF to be unloaded. To have a durable delay, the workflow needs to be unloaded-->
<workflowIdle timeToUnload="0"/>

1 行目では、SQL Server インスタンス ストアを使用するように、永続化のビヘイビアを構成しています。2 行目では、インスタンスがアイドル状態になったら直ちに、インスタンスを保存してアンロードするように、永続化フレームワークに指示しています (つまり、Receive を実行して応答を待機するアイドル状態になったら、ワークフロー インスタンスはアンロードされて、データベースに保存されます)。

ワークフローが保存されるように構成され、関連付けが構成されると、メッセージが到着した場合、ホスト (WorkflowServiceHost) によって関連付け情報を基に正しいインスタンスが読み込まれます。

customerCode を基に関連付けを行うように構成されているインスタンスがあるとし、customerCode = 43 であるとします。住宅金融会社に利率を問い合わせ、応答の待機中にインスタンスは保存されるとします (この場合、関連付け情報も保存されます)。金融会社から customerCode = 43 に対するメッセージが返されると、WorkflowServiceHost は自動的にインスタンス ストアから該当するインスタンスを読み込み、メッセージを送信します。

SQL インスタンス ストアは、既定ではインストールされていないことに注意してください。.NET Framework 4 で提供されている一連のスクリプトを実行して、明示的にインストールする必要があります。

サービスを追跡する

これで、メッセージ ベースの方法で、他のサービスと通信する処理時間の長いサービスを作成できました。このサービスは、永続的な構成で、処理を並行して実行し、非同期のアクティビティを実行できます。これは非常に複雑に思えます。問題が発生した場合はどうすればよいでしょう。どのアクティビティが失敗したかは、どのようにして確認するのでしょう。単にサービスのインスタンスの処理状態を確認する場合は、どうしたらよいでしょう。

Visual Studio ではワークフローのデバッグを実行できます (また、コードのデバッグと同様に、ブレークポイントを設定することで WF デザイナーでステップ実行によるデバッグを行うこともできます) が、これは運用環境では利用できません。

代わりに、WF には実行中のワークフローについてのデータを提供する、機能性の高い追跡インフラストラクチャが用意されています。追跡によって、ワークフロー内での処理内容 (追跡イベント) が、イベントを保存した追跡パーティシパントに通知されます。追跡プロファイルを使用すると、追跡パーティシパントが受け取るイベントにフィルタを適用して、必要な情報のみを取得できるようにすることができます。

WF 4 では、Windows イベント ログにデータを保存する追跡パーティシパント (EtwTrackingParticipant) が提供されます。TrackingParticipant を拡張することで、独自の追跡パーティシパントを作成できます。このワークフローでは、既定の EtwTrackingParticipant を使用しています。EtwTrackingParticipant を使用するためのコードを作成する必要はありません。web.config ファイルで適切な構成を行うだけで使用できます。ここでは、まず、EtwTrackingParticipant を使用するようにサービスを構成します。

<!--Set up ETW tracking -->
<etwTracking profileName="HealthMonitoring "/>

また、サービスの状態を評価するためのイベントを提供する HealthMonitoring プロファイルを使用するように設定します (図 9 参照)。これで、サービス状態の監視と、問題が発生した場合に問題の解決に役立つイベントについての情報が提供されるようになりました。SDK には、独自の追跡パーティシパントの作成方法と、トラブルシューティング プロファイルを作成する方法を示すいくつかのサンプルが含まれています。

図 9 追跡パーティシパント プロファイルの指定

<tracking>
  <profiles>
    <!--The health monitoring profile queries for workflow instance level records and for workflow activity fault propagation records-->
    <trackingProfile 
      name="HealthMonitoring">
      <workflow activityDefinitionId="*">
        <workflowInstanceQueries>
          <workflowInstanceQuery>
            <states>
              <state name="Started"/>
              <state name="Completed"/>
              <state name="Aborted"/>
              <state name="UnhandledException"/>
            </states>
          </workflowInstanceQuery>
        </workflowInstanceQueries>
        <faultPropagationQueries>
          <faultPropagationQuery 
            faultSourceActivityName ="*" 
            faultHandlerActivityName="*"/>
        </faultPropagationQueries>
      </workflow>
    </trackingProfile>
  </profiles>
</tracking>

サービスを配置して利用する

ここまでで、デザイナーを使用してワークフローを作成し、永続化と追跡を使用するようにワークフローを構成しました。唯一残された作業は、サービスをホストし、実行することです。

開発中は、Visual Studio 2010 の組み込みの Web サービス ホストを使用してサービスをホストできます。それには、サービスを使用してプロジェクトを実行するだけでよく、他の作業は必要ありません。

運用環境でサービスをホストする場合の処理も、ほんの少し複雑になるだけです。WCF ワークフロー サービス は、IIS または AppFabric アプリケーション サーバー拡張機能によりホストします。Visual Studio 2010 の場合、IIS に直接インポートできるパッケージを作成できます。AppFabric を使用すると、サービスのインスタンスについての概要を確認したり、記録されている追跡情報を照会できるダッシュボードなどの機能を利用できます。

最後に、実際にサービスを使用します。このシナリオでは、Contoso は Web ベースのインターフェイスを使用して、ユーザーがサービスとやり取りできるようにすることを望んでいます。つまり、ASP.NET アプリケーションからサービスを利用します。

ここで紹介しているサンプル WCF ワークフロー サービスは、コードで記述できる、他の WCF サービスとまったく同じです。このサービスを利用するには、サービス参照をクライアント プロジェクトに追加する必要があります。このサービス参照によって、サービスを呼び出すクライアント プロキシが作成されます。

参照を用意できたら、サービスを呼び出すことができます。このサービスのクライアントには、サービスの Receive アクティビティごとに 1 つの操作が実装されています。図 10 は、住宅ローンの承認申請に使用されるコード (従って、サービスの新しいインスタンスを開始するコード) です。

図 10 サービスの利用

protected void OnSubmit(object sender, EventArgs e) {
  using (ContosoRealEstate.ContosoRealEstateClient client = 
    new ContosoRealEstate.ContosoRealEstateClient()) {

    string message = "";        
    string result = client.EvaluateMortgage(
      out message,
      this.txtId.Text,
      Convert.ToInt32(this.txtHousePrice.Text),
      Convert.ToInt32(this.txtDownpayment.Text),
      Convert.ToInt32(this.txtYears.Text),
      Convert.ToInt32(this.txtSalary.Text),
      this.chkCreditHistory.Checked,
      this.chkBankrupcy.Checked,
      this.chkLawsuit.Checked,
      this.chkForeclosure.Checked);
    lblMessage.CssClass= result;
    lblMessage.Text = message + " (" + result + ")";

    this.btnSubmit.Visible = result.Equals("Incorrect");
    this.btnMonitor.Visible = result.Equals("Approved");
  } 
}

まとめ

ご覧いただいたように、.NET Framework 4 では、既存のコンポーネントを組み合わせて複雑で実用的なソリューションを作成するために使用できる多様な機能を備えています。また、さまざまなシナリオに対応するため、特定のニーズに合わせてそれらのコンポーネントを調整できるように、拡張ポイントも用意されています。

必要に応じで独自のコードを作成することもできますが、WCF ワークフロー サービスでは、既存のアクティビティを組み合わせてサービスを構成するだけで、処理期間が長く、持続性が必要な、インストルメント化されたプロセスを宣言的に記述できます。

今回のコラムでは、WF 4 と WCF 4 のいくつかの機能を組み合わせて、サービス固有の作業を実行し、他の既存のサービスとの対話を調整するサービスを作成しました。新しい成果物 (カスタム アクティビティ) も含めて、このサービス全体をコードを記述しないで作成しました。

これらの .NET Framework 4 テクノロジを使用してできることは他にも多数あります。

Leon Welicki は、マイクロソフトの WF チームのプログラム マネージャーで、アクティビティと WF ランタイムを担当しています。マイクロソフトに入社する前は、スペインの大手通信企業でリード アーキテクトと開発マネージャーを務めていたほか、マドリードのサラマンカ カトリック大学の大学院でコンピューター サイエンスの客員助教授を務めていました。

この記事のレビューに協力してくれた技術スタッフの Dave Cliffe、Vikram Desai、Kavita Kamani、および Bob Schmidt に心より感謝いたします。