WF 標準アクティビティ (Base Activity Library) の歩き方


対象フレームワーク : .NET Framework 3.5

(2008/08/05  ビデオ更新に伴い、ビデオのリンクを修正) 

こんにちは。

WF (Windows Workflow Foundation) は、プロセスの再利用と柔軟性をモデル駆動で実装するためのフレームワークです。WF は、基本的にはベースのエンジンであり、典型的な使い方としては、再利用性の高いアクティビティを開発者が作成し、これらを組み合わせて作成していきます。

しかしご存知の通り、0 からすべて作成する必要はなく、デフォルトでいくつかの使えるアクティビティを Base Activity Library として提供しています (開発者が作成するカスタムアクティビティの中でもこれらを使用することができます)。

そこで今日は、これら標準のアクティビティが何のために存在し、どんな風に活用できるかを理解していただきたいとと思います (既にあるものは、作る必要はありません)。Replicator や Sequence、EventHandlingScope などは SharePoint でも使用できますので、おぼえておいて損はないでしょう。
よく使いそうなものから順に記載します。

Code

C#, VB.NET などで記述されたコードを実行するというもっとも一般的で、普通なアクティビティです。
要は、単に「処理を実行する」アクティビティです。

IfElse

分岐を実現するためのアクティビティです。
分岐処理がなぜアクティビティなのか疑問に持たれる方も多いでしょう。アクティビティは、その内部にアクティビティを持たせて階層化させることができます。これを Composite アクティビティと呼びますが、IfElse も、実際に分岐するアクティビティ(処理)を中に入れる必要がありますので、Composite アクティビティの1つとして実装されていて、このアクティビティの中に実際の処理をアクティビティとして組み込んで使います。

分岐の追加をおこなっていくことで、3つ以上に分岐させることもできます (ネストさせる必要はありません)。

While

While ループを実装するためのアクティビティです。
こちらも、実際のループする処理をアクティビティとして含める必要がありますから、Composite アクティビティ (上述) の1つとして実装されています。

HandleExternalEvent

ワークフローインスタンスは、複数実行や永続化などを考慮して、デフォルトでは、呼び出し元のホストスレッドとは別のスレッドとして動作します。
そこで、これらインスタンスのスレッドとホスト間でデータやメッセージの交換を実行中におこなうためには独自の仕組みが必要になってきます。そのためのアクティビティがこのアクティビティです。HandleExternalEvent を使うと、イベントのメカニズムを使って、ホスト側からインスタンス側に要求やデータをイベントとして渡すことができます。(永続化されているインスタンスも自動的にロードしてくれます。)

この仕組みは、現実のアプリケーション開発では必ず必要になるはずです。
使い方ははじめての方にとってはややわかりづらいかもしれませんが、典型例として以下の Web キャストを作成していますので参照してください。

http://www.microsoft.com/japan/msdn/events/online/study/vssolution/servicedevelopment/code/wf.aspx

CallExternalMethod

上記の HandleExternalEvent に説明したインスタンス上のスレッドとホスト上のスレッドを連携させるアクティビティのもう1つで、CallExternalMethod は、HandleExternalEvent とは逆に、インスタンス側からホスト側への呼び出しをおこないます。(上記のビデオを参照)

State

ステートマシンワークフローにおいて、各ステート (状態) を定義するためのアクティビティです。

ステートマシンワークフローでは、配置するアクティビティとして、必ずこの State を入れて作成し、各 State の中に StateInitialization アクティビティ、EventDriven アクティビティ (複数可)、StateFinalization アクティビティ を配置して State を組み立てます。(State アクティビティは、Composite アクティビティ (上記) です。)

EventDriven

イベントによって稼動開始する一連の処理を作成するための Composite アクティビティ (上記) です。
よって、この子アクティビティには、必ず最初に、HandleExternalEvent などのイベントをハンドリングするアクティビティを挿入しておく必要があります (あるいは Delay アクティビティも使えます。詳細は Delay を参照してください)。

シーケンシャルワークフローでも使いますが、特にステートマシンワークフローでは状態遷移型で実装するため、必須のアクティビティです。(State を参照)

SetState

ステートマシンワークフローにおいて、どの State に遷移するかを指定するアクティビティです。

よって、一般には、ステートマシンワークフローにおける EventDriven の中の子アクティビティの最後の処理 (遷移先が決定した箇所) で使用されます。(但し、完了ステートの中では、無論、使われません。)

Listen

複数のイベントのいずれかを待機するためのもので、発生したイベントによってどれか1つの分岐が実行されます。(他の分岐は実行されません。) よって、子アクティビティとして、必ず上述の EventDriven アクティビティを挿入します。

ステートマシンワークフローでは、上述の通り State アクティビティによりこれと同等の処理がおこなわれますので、Listen は、主にシーケンシャルワークフローでこうしたイベント駆動による分岐が必要になった場合に使用されます。

Delay

タイムアウト時間を設定して、その時間の間、インスタンスをアイドル状態にします。(アイドルイベントが発生します。)

たいした仕事をしないアクティビティのように見えますが、用途は、実は大変幅広いです。
例えば、永続化サービスを使う場合に、アイドル状態になったらアンロード (インスタンスをメモリからストアに退避) する
という指定ができます。このような場合に、アンロードを目的として Delay アクティビティを挿入するといった使い方ができます。他にも、EventHandlingScope アクティビティを使って、一定の時間、ホストからのリクエストを受け付けたいという場合にも、EventHandlingScope アクティビティのメインの処理に Delay アクティビティを挿入するといった使い方ができるでしょう。また Delay は、IEventActivity というインタフェースを実装しているため、ステートマシンワークフローにおける EventDriven において、特に受け付けるイベントが存在しない場合などには、このアクティビティを挿入しておくことができます。(EventDriven は、IEventActivity というインタフェースを実装したアクティビティを必ず最初に入れておく必要があるためです。EventDriven については上記を参照してください。)

Parallel

含まれている子アクティビティを非同期に(同時に)実行します。
アクティビティがすべて終了するまで、この Parallel アクティビティは終了しません。

StateInitialization

ステートマシンワークフローの State アクティビティの子アクティビティとして使用します (State を参照)。State アクティビティの中のあらゆる EventDriven アクティビティよりも先に、このアクティビティが呼ばれます。つまり、State の初期化(もしくは準備)の処理を実装したい場合に使用するアクティビティです。

StateFinalization

ステートマシンワークフローの State アクティビティの子アクティビティとして使用します (State を参照)。State アクティビティが終了してつぎの State に遷移する際にこのアクティビティが呼ばれます。つまり、State の終了処理を実装したい場合に使用するアクティビティです。

Sequence

Composite アクティビティ (上記) であり、この中に挿入したアクティビティを順次実行します。
子アクティビティを登録した順番に検索して実行する仕組みが実装されています。WF を使われている方はご存知かと思いますが、プロジェクトとして利用する「シーケンシャルワークフロー」は、SequentiqlWorkflowActivity という Composite アクティビティです。実は、SequentiqlWorkflowActivity は、この Sequence (アセンブリの中では SequenceActivity) を継承して作成されています。

用途ですが、例えば、Replicator や EventHandlingScope のように、子アクティビティを1つしか挿入できないアクティビティというものがあります。こうしたアクティビティに逐次実行の複数の処理を入れたい場合には、この Sequence アクティビティを使うことで解決できます。

Replicator

Composite アクティビティ (上記) であり、この中に挿入したアクティビティを複数作成して逐次実行や並列実行がおこなえます。つまり、同じ処理を複数実行する場合や、実行時にならないと実行する数が特定できない場合などは、このアクティビティを使って子アクティビティを1つだけ設定しておけば実現できます。

子を生成するための元データを List で渡し、かつ、子アクティビティが Initialize されるたびに (子の Initialize ごとに) イベントハンドラを呼び出せるので、処理のロジックが同じ(つまり、アクティビティとして同一)で内部で使うデータだけが違うような処理を複数個作成して実行することができるようになっています。

EventHandlingScope

特定の処理の実行中に、特定のイベントを任意に受け付けるような処理を実現できます。
EventHandlingScope は Composite アクティビティ (上記) です。EventHandlingScope に含めたアクティビティを実行している間じゅう、ずっとイベントを受け付けて別の処理を実行させることができます。メインの処理は EventHandlingScope の子アクティビティとして挿入しますが、イベントに応じたもう一方の処理は、このアクティビティを右クリックして表示される [イベントハンドラの表示] メニューを選択すると実装することができます。

例えば、承認ワークフローを実行している間、承認者のアサインの変更をいつでもできるようにしたいといった場合には、このアクティビティが使えるはずです。(SharePoint の ModificationForm (いつ呼ばれるかわからない変更フォーム) などは、まさしくこの仕組みで実装することができます。)

FaultHandler

WF では、エラー処理についても、モデル駆動で可変性を考慮した実装 (例えば、管理者やユーザに処理方法を変更させる、など) をすることができます。

WF のデザイナー上で、一番右下にある 「!」 マークのアイコン (タブ) をクリックすると、エラーハンドラー用のワークフロー作成ができるようになっています。ここの faultHandlersActivity1 の箇所に FaultHandler アクティビティをドラッグアンドドロップし、FaultHandler アクティビティの FaultType プロパティに処理する例外のタイプを指定することで、指定した型 (もしくはそこから継承された型) のエラーが発生すると、このエラー処理用のワークフローを実行することができます。(この場合、元のワークフローは thorw された箇所でストップします。)
また、FaultHandler を複数作成して、Exception に応じ別々のエラー処理を作成することもできるようになっています。

BPM やワークフロー関連のモデラーを実際に使われたことがある方は、ワークフローなどのモデル駆動な仕組みがいかにエラー処理とミスマッチかご理解いただけるでしょう。エラー時の処理も含めたフローを考えはじめると、ワークフローは分岐だらけで収集がつかなく (余計な処理が混在された形に) なってきます。
上述のように、WF では、こうした記述をメインのロジックと分離して管理することができるようになっています。

尚、こうした例外の処理がワークフローの中で記述されていない場合、例外の発生と共にワークフローインスタンスは終了し、ホスト側 (呼び出し側, ランタイム側) に例外が伝播されます。

Throw

例外 (System.Exception の継承オブジェクト) を Raise するアクティビティです。
ハンドラなどのコード (C#, VB.NET, などのコード) の中で、throw を実行しても同じですが、WF では、上述の FaultHandler アクティビティで記載したように、エラーのハンドリングについてもモデル駆動で定義して作成できるようになっており、エラーの発生の処理についても可変性を高めた実装方法を採用したい場合には、コードに埋め込まずにこの Thorw アクティビティを使って実装するといったことが可能になっています。

TransactionScope

アトミックトランザクションのトランザクションスコープを作成します。
Composite アクティビティ (上記) であり、スコープ内の処理は、この TransactionScope の中に必要なアクティビティをドラッグして作成していきます。
このスコープの中で Throw アクティビティを実行すると、トランザクションのロールバックが実行されます。(FaultHandler を参照)

ここで、あえて「アトミック」トランザクションと明記している理由は、WF では、後述する CompensatableTransactionScope で説明するように、永続化なども含めたロングトランザクションの一貫性保証を実現できる別の仕組みがあるためです。

CompensatableTransactionScope

承認ワークフローなど、現実のワークフローでは、日にちをまたがって実行される処理などが一般的ではないかと思います。こうした場合には、アトミックトランザクションの仕組みは使用できないため、補正トランザクションといって、処理をもとに戻すための仕組み(コードなど)を記述して、エラー発生時は、ロジックにより元に戻すといった処理を構築する必要が出てきます。

さて、この補正の処理を上述の FaultHandler だけで書いてしまった場合、どのような事態になってしまうでしょうか?補正トランザクションが複数存在している場合を想像してみてください。おそらく、FaultHandler の中には、いくつかの補正のためのたくさんのアクティビティが並び、エラーのワークフローは、ついに収集がつかないほど巨大になってしまうことでしょう。
CompensatableTransactionScope は、この補正の処理をスコープ単位にまとめて扱うことができます。

補正を組み込むには、CompensatableTransactionScope アクティビティを作成し、TransactionScope と同様、内部の処理を記述します。但し、CompensatableTransactionScope で1つの完結した処理として扱いますので、例外は、このスコープを出てから Throw するようにしてください。
つぎに、この CompensatableTransactionScope アクティビティを右クリックして、[補正ハンドラの表示] を表示すると、この補正トランザクションにおける補正の処理 (データなどを元に戻す処理で、ここは、無論、業務要件にあわせて逐一作成する必要があります) を記述できます。例外が Throw されると、補正処理が呼び出されます。

かなり賢くできていて、CompensatableTransactionScope1 と CompensatableTransactionScope2 を実行したあとで Throw されると、2 の補正処理が先に呼ばれ、つぎに 1 の補正処理が呼ばれます。同じフローで、今度は、CompensatableTransactionScope1 だけが実行されたあとで例外が Throw されると、1 の補正処理だけが呼ばれるといった具合に、内部で補正の内容と順序が解釈されます。

FaultHandler のハンドラを作成している場合には、無論、この補正の処理は呼び出されません。この場合は、FaultHandler のハンドラの処理の中で、後述する Compensate アクティビティを挿入することで補正処理を呼び出すことができます (Compensate アクティビティ には、どの CompensatableTransactionScope アクティビティの補正処理を実施するか設定しておきます)。この場合は、どの CompensatableTransactionScope アクティビティの補正処理を呼ぶべきか開発者が判断する必要があるので注意してください。
FaultHandler の処理を含んでいる場合には、例外が発生すると、とまらずにハンドラの処理に入ってしまうので、この場合、補正処理のデバッグなどは少々大変かもしれません(実際に開発してみるとわかります)。また、Compensate による補正処理の実行は、デバッグ実行では正しく呼ばれませんので注意しましょう ([Ctrl] + [F5] で動作確認してください)。

なお、CompensatableTransactionScope は、永続化サービスと一緒に使う必要があります。(でないと、エラーになります。) 永続化サービスの使い方については簡単ですので、以下の Web キャストを参照してください。(さいごの10分くらいで使っているだけなので、バーを後ろに送ってから再生してください。)

http://www.microsoft.com/japan/msdn/windows/windowsserver2008/tab/code/eds.aspx

CompensatableSequence

いわば、CompensatableTransactionScope の Sequence アクティビティ版です (SequenceActivity を継承しています)。
CompensatableTransactionScope と同様に、例外の Throw によって、補正処理が呼び出されます。但し、CompensatableTransactionScope と異なり、アクティビティイベントをリスンした、いわゆるトランザクションの作成はおこなわないので、CompensatableTransactionScope に比べリソースのオーバーヘッドは高くありません。

こちらも、CompensatableTransactionScope と同じ注意点 (永続化サービスと使う、など) があてはまります。

Compensate

CompensatableTransactionScope を参照してください。

Terminate

ワークフローインスタンスを強制終了します。
デフォルトの設定では、インスタンスは、最後まで処理がおこなわれると自動的に終了しますので、このアクティビティは、主にエラー発生時 (途中で強制終了したい場合) などに使用することになるでしょう。

Suspend

ワークフローを中断します。Terminate と違い、また再開できます。

例えば、インスタンス内部のエラーやインシデント(例えば、ループが所定の回数を超えた場合、など)の発生時に、一旦終了してホスト側に修正や判断を要求したい場合などに、この Suspend を使って一旦インスタンス側から中断し、ホスト側で中断 (Suspend) のイベントをハンドリングして処理をおこない (例えば、エンドユーザに通知して、データがそろってから再実行するよう促す、など)、開始できる状態になったらホスト側 (ユーザ側) からインスタンスの Resume メソッドを呼び出して再実行させる、といった具合に使えます。

ConditionedActivityGroup

Composite アクティビティ (上記) であり、IfElse のように条件 (変数の値、あるいはコードを使って評価) によって、条件にマッチする子アクティビティを実行することができます。
さらに、ConditionedActivityGroup は、Until 条件も設定することができ、Until 条件が条件をみたすまで評価と子アクティビティの実行を繰り返すことができます。

このアクティビティですが、厳密には、IfElse (上記) や While (上記) を組み合わせれば実現できるので (IfElse などについても、分岐の条件として宣言型のルール条件を設定することができます)、必要ないと思われるかもしれません。しかし、例えば、ConditionedActivityGroup は、評価に一致する子アクティビティが複数ある場合でも、すべてを実行してくれます。こうしたケースを IfElse や While といった方法で作成するとモデルが複雑になってきます。
こうしたルールに沿った実行は、IfElse などの手続き型ではなく、こうして宣言的に作成することによって、意味上のモデルにマッチしたフローを組み立てることができるのです。

Policy

ConditionedActivityGroup アクティビティ (上記) 同様に、ルールベースで処理を作成できるもう1つのアクティビティです。
こちらは、さらに高度なことが可能で、ルールセットエディタと呼ばれるものを起動して、例えば、「Price 値が 200000 以上で 500000 未満なら、Risk 値に 3 を設定し、それ以外なら Risk 値 に 1 を設定する」 などといった、複雑なルールを複数個設定することができるようになっています。さらにはエディタ上で、有効/無効の切り替えや、優先度の設定など(優先度の高いものが先に評価され、実行されます。)、より複雑なルールベースの設定が可能です。(尚、ルールエンジンを既存のエディタではなく他の手段で提供したい場合には、提供されている API を使用してルールエンジンをカスタムに実装するということも可能です。)

ConditionedActivityGroup アクティビティ (上記) 同様、ビジネスルールを1つのアクティビティに宣言的にまとめて管理したい場合に使用します。

Policy を使ったサンプルについては、「Windows Workflow Foundation ハンズオン ラボ」 の 「演習07 - ルールベースのワークフローの作成」に、簡潔に書かれています。

尚、作成したルールの情報は、ルールファイルという XML のテキストとして保存されます。

InvokeWorkflow

同一プロジェクト、もしくは参照先のアセンブリの別のワークフローのインスタンスを実行します。
パラメータを渡すことはできますが、呼び出されたワークフローインスタンスは非同期に実行されるため、結果 (OutputParameter) を取得することは (一般的な方法では) できないので注意してください。(データ交換を使った特別な仕組みを実装する必要があります。)

WebServiceInput

作成したワークフローを Web サービスとしてラッピングし、公開して使えるようにするための仕組みをもっています。
作成方法の概略を述べると、ここに記述している WebServiceInput アクティビティで Web サービスの引数として受け付けている値をインスタンス上で取得し、WebServiceOutput で逆に Web サービスで return する値を設定し、構築後に、Visual Studio のソリューションエクスプローラのプロジェクトを右クリックして [Publish as a Web Service] を選択することで Web サービスとして利用可能な dll が作成され、Web サービスとして利用することができます。この際、Web サービスとして公開するインタフェースも定義しておく必要があるため、interface を定義して、WebServiceInput の引数や WebServiceOutput の返り値 (return) とこの interface のメソッドメンバとの関連付けなどもおこないます。
これらはすべて、Visual Studio のデザイナー上から設定できます。(無論、interface 定義は、コードとして書く必要がありますが)

WebServiceOutput

WebServiceInput を参照してください。

WebServiceFault

Web サービスのラッピング (WebServiceInput を参照) で、SOAP Exception を発行する場合にこのアクティビティを使用します。
例えば、処理の結果に応じ、分岐 (IfElse) を作成し、片方は成功 (WebServiceOutput)、片方は失敗 (WebServiceFault) といった具合に使用します。

InvokeWebService

Web サービスを呼び出すアクティビティです。
ワークフローの Web サービスラッピング (WebServiceInput を参照) と併用すれば、HTTP によるワークフローどうしの連携シナリオ (例:「・・・組合のサービスで処理を受け付け、実際の発注処理は、各店舗の提供するサービスに処理を渡す」など) も可能となります。

こうした連携シナリオは、Visual Studio 2008 でさらに充実していますので、新機能を知りたい方は以下を参照してみてください。

http://www.microsoft.com/japan/msdn/events/online/study/vssolution/servicedevelopment/code/wcf_wf_integrated.aspx

SynchronizationScope

ワークフローの中で宣言されたスタティックな変数 (C# の static 宣言、もしくは VB.NET の shared 宣言された変数)
に複数インスタンスからアクセスをおこなうと、同一のメモリアドレスにアクセスするため、一般にはアクセスが競合してエラーとなります。
SynchronizationScope はComposite アクティビティ (上記) であり、この中の子アクティビティから static な変数にアクセスすると、この競合を回避し、シンクロナイズされたアクセスを保証 (平たく書くと、インスタンス間でぶつからないように制御) してくれます。

ReceiveActivity (.NET Framework 3.5 から)

.NET Framework 3.5 より、WCF で要求を受け取り WF で処理して返す、といったような WCF / WF の連携が "簡単に" おこなえるクラス (及び、アセンブリとネームスペース) が追加され、こうした連携アプリを簡単に作成できるようになっています。このアクティビティは、その追加されたアクティビティで、アクティビティのプロパティの ServiceOperationInfo 属性で、WCF インタフェースのオペレーションを指定することで、WCF 側で該当のオペレーションが呼び出された場合にこのアクティビティが処理されるように動作します。(CanCreateInstance 属性を true に設定すると、該当の WCF のオペレーションが呼ばれた場合に WF のインスタンスを開始させることができます。)

SendActivity (.NET Framework 3.5 から)

逆に、WF の中から WCF 側に処理を要求する際に使用するアクティビティです。
これらのアクティビティとコンテキスト (Context) と呼ばれる内部オブジェクトを使用して、ワークフローどうしの対話型の通信を実現することも可能です。

 

尚、開発者が作成するアクティビティも、実行エンジンが持つイベントメカニズムやスケジューリングメカニズムにアクセスすることで、単なる処理の開始・終了以上にもっと応用性の高い、ここに記載されているような高度なアクティビティをカスタムに組み込むことができます。

 

Comments (2)

  1. WF 標準アクティビティ (Base Activity Library) の歩き方 WFが標準で用意しているアクティビティをそれぞれどのような用途に使えるかがまとめられています。んー、はやく使ってみないとなぁ。。…

  2. ※ 以下、Tech ED T-405 セッションに関するフォローアップ記事 Part 3 です。 こんにちは。 この内容(と、次の Part 4)は、ECM Starter Kit のサンプルで充分ですので、特にコードはありません。

Skip to main content