はじめに
Workflow Foundation(以下、WF)は.NET 3.0にて提供されましたが、.NET 4になり更新されたWF 4はそれまでのWF 3とは全く異なるモデルを採用したこともあり、内部構造および利用方法はそれまでと別物になりました。今回はWF 4における中心要素、アクティビティの開発について紹介します。
対象読者
- 異なる開発手法に興味のある方
- Workflow Foundationに興味のある方
- VB、C#でプログラミングを行ったことがある方
必要な環境
サンプルコードを実行するには、Express Editionを含むVisual Studio 2010が必要です。
デザイナー上でワークフローを作成するには、Professional以上のVisual Studio 2010が必要です。ただしWF 4ではワークフローデザイナーの自作が容易になっており、Experss Editionでも作成可能です。
ワークフローの中心要素・アクティビティの種類
WF 4において中心要素となるアクティビティは、大きく分けて3種類用意されています。シンプルに処理を実行するCodeActivity、非同期で処理を実行するAsyncCodeActivity、そしてより高度な制御を行うことができるNativeActivityです。
また、コードで実装する方法とコードを利用せずワークフローとして実装する方法、2通りの方法を用いることができます。ワークフローとして実装するにはProfessional以上のVisual Studio 2010か、自作したワークフローデザイナーが必要となります。ワークフローデザイナーの自作については次回で紹介するので、今回はコードで実装する方法を解説します。
基本形となるCodeActivityでの実装
まずVisual Studio 2010を起動し、新規にコンソールプロジェクトを作成します。Professional以上のVisual Studioの場合、プロジェクトのカテゴリにワークフローが用意されているので、そちらから新規に作成しても構いません。
今回の場合、新規にワークフローコンソールアプリケーションを選択します。その際に作成されるプロジェクトではWorkflow1.xamlという名前でワークフローが用意されていますが、今回の実装では利用しないので削除してください。
新規にプロジェクトが作成されると、IDE上にはModule1.vb(またはProgram.cs)が表示されている状態になります。VBではSub Main()、C#ではstatic void Main(string[] args)、と書かれている部分がアプリケーションの実行メソッドとなるエントリポイントです。
Express Editionなどでコンソールプロジェクトとして新規に作成した場合、WF 4関係の実装を行うためには関連するライブラリの参照設定が必要です。System.Activities、System.Activities.Presentation、System.Activities.Core.Presentationの3ライブラリに対して参照設定を行います。似た名前空間のライブラリとして、System.Activities.DurableInstancingがありますが、このライブラリは今回利用しないため、参照設定は必要ありません。
まずはCodeActiityを利用してアクティビティの実装を行います。新規にクラスを追加し名前を適当につけます(今回のサンプルではSampleCodeActivityとしています)。そしてSystem.Activities.CodeActivityクラスを継承します。継承を行ったクラスにはExecuteメソッドのひな形が用意され、ここに実行する処理を記述します。今回は簡単なサンプルとして、プロパティを通して受け取った文字列を標準出力に出力する処理を記述します。
Imports System.Activities Public Class SampleCodeActivity Inherits CodeActivity '受け渡しに利用するプロパティ Public Property OutputStrings As InArgument(Of String) 'アクティビティの処理 Protected Overrides Sub Execute(context As System.Activities.CodeActivityContext) Console.WriteLine(context.GetValue(Me.OutputStrings)) End Sub End Class
using System; using System.Activities; namespace ConsoleApplication1 { public class SampleCodeActivity : CodeActivity { ///受け渡しに利用するプロパティ public InArgument(Of String) OutputStrings {get; set;} ///アクティビティの処理 protected override void Execute(CodeActivityContext context) { Console.WriteLine(context.GetValue(this.OutputStrings)); } } }
上記のような実装を行うと、プロパティを通して受け取った文字列を標準出力に出力するアクティビティとなります。単純なサンプルですが、ここには通常の実装と異なるWF固有のポイントがあります。それはプロパティの実装部分で、文字列を受け取るOutputStringsプロパティの型をInArgumentとして記述しています。これはWFの実行制御と大きく関わるポイントで「アクティビティは常に新規生成するのではなく複製され使い回される」という挙動があります。これはループ系のアクティビティで利用すると明確になりますが、一度利用されたアクティビティの複製が作成され再利用されてしまうので前回の処理結果を引き継いでしまいます。
今回のサンプルでは影響がありませんが、アクティビティ内部で計算を行い結果を保持するようなケースではバグとなります。それを回避するために用意されているのがInArgumentやOutArgumentといったArgumentクラス群となります。こちらを利用することで、実行時にWFランタイムにより必ず初期化が行われ、値の受け渡しが行われるようになります。このような理由もあり、アクティビティのプロパティはArgumentクラスを利用するのが基本となります。
もう1つのポイントは実際の処理が記述されているExecuteメソッド内部でcontext.GetValueメソッドを利用している箇所です。この部分は先ほどのArgumentを利用したプロパティに設定されている値を取得しています。WFランタイムが用意している実行時コンテキストであるActivityContextクラス(またはその派生クラス)には、ランタイムが管理しているさまざまな情報が設定されています。GetValueメソッドを用いることで、実際にアクティビティに渡された値を取得できます。反対にプロパティに値を設定する際にはSetValueメソッドを用います。