いまさらながらの
  Windows Workflow入門
~そして実際にWFを実務に活用してみて思ったこと~




               株式会社アジャスト
               http://www.adjustgroup.co.jp
Agenda
 • まずはコーディングして体を慣らします。

   – Hands On


 • なぜ、WF のシーケンシャルワークフローを使うのか?

 • そのほか

   – はまったこと

   – ナゾ

   – 参考サイト
まずはコーディングして
体を慣らします。
開発者の視点では "ワークフロー" って何?
• つまるところは .NET のクラス(ないしはそのインスタンス)です。


• Visual Studio のデザイナ画面でグラフィカルにデザインできますが、
 究極的には、C#ソースコードを書いているに過ぎません。
“アクティビティ” について
• アクティビティ(Activity) = 活動、動作

• ワークフローを構成する最小要素のこと。

• これもつまるところ、.NET クラス(ないしはそのインスタンス)のこ
 とです。

• Activity クラスないしはその派生クラスになります。

• Visual Studio は大変賢く、”アクティビティ” クラスを適切にツールパ
 レットに表示します。
Hands On
• プロジェクトの新規作成 > Visual C# > Windows > WPF アプリケーション
  を作る。

• ソリューション右クリック > 新しいプロジェクトの追加 > Workflow > シー
  ケンシャルワークフローライブラリ を作る。

• WpfApplication1 で、WorkflowLibrary1 への参照設定を済ませる。

• ビルドを確認。



• デザイナ画面に Delay をDropしてDurationに3秒を設定。

• デザイナ画面に CodeActivity の ExecuteCode をハンドル、参照設定で
  PresentationCore と PresentationFramework を追加し、「
  MessageBox.Show("OK");」をコーディング。



• ここまでやって、Workflow1.Designer.cs を開いてみる。
WFを使うプログラムの構造
• WorkflowRuntime オブジェクトを new して使う。

• ワークフローの実行は WorkflowRuntime オブジェクトが司る。

• 開発した"ワークフロー"クラスは、自分で new しちゃダメ。

• WorkflowRuntime オブジェクトに、開発した"ワークフロー"の Type
 を指定して、その"ワークフロー"をインスタンス化(new)してもらう。

• WorkflowRuntime オブジェクトから、new してもらったワークフロ
 ーオブジェクトを内包する、WorkflowInstance オブジェクトが返さ
 れるので、 その WorkflowInstance オブジェクトの Start メソッドで
 、そのワークフローの実行が開始!
Hands On
• WpfApplication1 プロジェクトにて、
  System.Workflow.Activities,ComponentModel,Runtime を参照設定追加。

• Window1.xaml のコードを表示、WorkflowRuntime の private メンバ変数
  _wfRuntime を用意。

• コンストラクタ Windows1 にて WorkflowRuntime を new して
  _wfRuntime に設定。

• Window1.xaml のデザイナ画面にて、Window の Unloaded イベントをハン
  ドル、_wfRuntimeの StopRuntime メソッドを呼び出し。



• Window1.xaml のデザイナ画面にて、ボタンを1個配置、Clickイベントハンド
  ラを作成。

• Click ハンドラ内に、CreateWorkflow 呼び出しを記述、返ってきたワークフロ
  ーインスタンスに対して Start メソッド呼び出し。

• ビルドして実行。
ワークフローは別スレッドで実行されます
• なので、ワークフローオブジェクトの Start メソッドは、その呼び出
 しからすぐに呼び出し元に返ります。

• 同期のコーディングが必要。

• WorkflowRuntime オブジェクトから、「ワークフローの実行が完遂
 したよ」などのイベントが発行されますので(※ワークフローオブジ
 ェクトからではない)、予めイベントハンドラを設定しておいたりし
 ます。
Hands On
• Window1.cs のコード表示。


• コンストラクタ Window1 () にて、_wfRuntime の WorkflowCompleted イ
  ベントハンドラを追加。


• 同イベントハンドラ内で、MessageBox 呼び出しをコーディング。
どうやって引数を渡すの?
• "ワークフロー" のプロパティを介在します。

• "ワークフロー" を開発するときに、受け取りたい引数は、"ワークフロ
 ー" のプロパティとして実装します。

• 次にワークフローを呼び出すアプリ側では、渡したい引数を、
 Dictionary<string, object> によるキーバリュー形式の辞書オブジェ
 クトにまとめます。

• この辞書オブジェクトを、WorkflowRuntime オブジェクトに "ワーク
 フロー" をインスタンス化(new)してもらうときに同時に、インスタン
 ス化メソッドの引数に渡します。

• すると、ワークフローオブジェクトの同名のプロパティに、辞書で渡
 された値が設定されて、ワークフローを開始可能となります。
Hands On
• Workflow1.cs のコードを表示。

• Workflow1 クラスに string Message プロパティ追加。

• codeActivity1_ExecuteCode ハンドラ中の MessageBox.Show 呼び出しにて
  、この Message プロパティを表示するように改造。



• Window1.xaml のコード表示。

• button1_Click にて CreateWorkflow 呼び出しの前に、辞書構築のコードを追
  加。

• CreateWorkflow の第二引数に追加。



• ビルドして実行。
カスタムアクティビティ
• どうということはなく、自前で Activity クラスから派生したクラスで
 す。

• Execute 仮想メソッドをオーバーライドして、そこに自由にコードを
 書きます。

• ビルドしたら、Visual Studio のツールボックスに表示されるようにな
 ります。

• ワークフローにDrug&Dropできます。
Hands On
• WorkflowLibrary1 プロジェクトにて右クリック > 追加 > アクティビティ >
  Activity1.cs を追加。

• デザイナ画面がひらいちゃうけどいったん閉じる。

• Activity1.cs のコード表示。

• 派生元を SequenceActivity からただの Activity に書き換え。

• Execute メソッドのオーバーライドを追加。

• 「MessageBox.Show("OK")」を記載。

• いったんビルド。



• Workflow1.cs のデザイナ表示。

• すべてのアクティビティをいったん削除。

• ツールパレット開くと、Activity1 が表示されているので、これをDrop。

• ビルドして実行。
どうやって引数を渡すの?
• カスタムアクティビティにプロパティを用意して、
 これを介在します。

• しかし!

• 普通にプロパティを実装するだけではダメ。

• 「依存プロパティ(Dependency Property)」というコーディングを行
 う必要があります。

• こうしておくと、デザイナ画面にて、依存プロパティのバインド先が
 指定できるようになります。

• こうして依存プロパティのバインドによって、引数を受け渡していき
 ます。
依存プロパティのコーディング
• DependencyProperty オブジェクトで "プロパティの定義" を作成。
  – プロパティの名前(string)
  – プロパティの型(Type)
  – どのクラスに備わっているプロパティなのか(Type)

• これを、static な public メンバ変数で公開。
• プロパティの実装コードでは、get, set それぞれに、Activityクラスの
 GetValue, SetValue メソッド呼び出しを記述。
• このときに、先に用意した "プロパティの定義"、すなわち、
 DependencyProperty オブジェクトを、GetValue, SetValue メソッ
 ドの引数に渡す。
• ...というコードをスクラッチで書くのはありえないので、コードスニ
 ペット使います。
Hands On
• Activity1.cs のコード表示。

• コードスニペット使う。NetFX30 から。

• string な WhatToShow 依存プロパティを実装。なお、UIPropertyMetadata
  が余計なので削除。

• Exeute オーバーライドメソッド内の MessageBox 呼び出しにて、
  WhatToShow 依存プロパティの値を表示するよう改訂。

• いったんビルド。



• Workflow1.cs のデザイナ表示。

• activity11 を選択すると、プロパティウィンドウに "WhatToShow" が増えて
  おり、[...] が押せる。

• [...] を押すとバインドのウィンドウが表示されるので、ここで、Workflow1 ク
  ラスの Message プロパティを選択する。

• ビルドして実行。
依存プロパティについてもう少し...
• 受け取るだけではなく、書き換えてもよいわけです。

• 後続するアクティビティは、書き換えられたプロパティ値を使うこと
 になります。

• ワークフローの"結果"、言うなれば、"戻り値" も、(ワークフローを
 開始するときと同じように)プロパティ経由で受け取ります。

• WorkflowCompleted イベントの時点で、イベント引数
 WorkflowCompletedEventArgs に、OutputParameters プロパティ
 という辞書の形で返ってきます。

• とにかく、プロパティを媒介として処理をリレーしていくイメージで
 す。
Hands On
• WorkflowLibrary1 プロジェクトにて右クリック > 追加 > アクティビティ >
  AddBlacketActivity.cs を追加。

• デザイナ画面がひらいちゃうけどいったん閉じる。

• AddBlacketActivity.cs のコード表示。

• 派生元を SequenceActivity からただの Activity に書き換え。

• string WhatToAddBlacket な依存プロパティ追加。

• Execute メソッドのオーバーライドを追加。

• 「WhatToAddBlacket = "[" + WhatToAddBlacket + "]"」をコーディング。

• いったんビルド。
• Workflow1.cs のデザイナ表示。

• ツールボックスにAddBlacketActivity が増えているので、これを activity11 の
  上に Drop。

• addBlacketActivity1 を選択し、プロパティウィンドウを開き、
  WhatToAddBlacket 依存プロパティのバインドを指定、Workflow1.Message
  にバインド。



• ビルドして実行、メッセージが "[~]" で囲まれていることを確かめる。
• Windows1.xaml のコード表示。

• _wfRuntime_WorkflowCompleted 内を
  var message = e.OutputParameters["Message"] as string;
  MessageBox.Show("Complete. Message is "+ message);
  に書き換え。



• ビルドして実行し、ワークフロー終了後のメッセージにて、、メッセージが "[
  ~]" で囲まれていることを確かめる
なぜ、WF のシーケンシャル
ワークフローを使うのか?
一般的に言われていること
• アクティビティの組み合わせをデザイナ画面上でちょちょっと変える
 だけですむので、次のようなメリットがあります。
 – 仕様変更に対する耐性が高くなる

 – カスタマイズ性能が向上する



• プログラマではなく、エンドユーザー自身がプログラムの動作を変更
 することができるようになる可能性が開けます。
実際にやってみて、それ以上に思ったこと
• "コードの可視化" というのは、並列処理化において強力であった!
• 「あ、このアクティビティとこのアクティビティは、依存関係がない
 から、同時に走らせていいや」ということに気づけるようになった!
• そして、.NET 備え付けのアクティビティ=Parallel アクティビティを
 組み合わせることで、デザイナ画面上で簡単に処理を並列化すること
 ができる!
• さらに Replicator アクティビティを Parallel モードで使うと、配列な
 どの各要素に対して繰り返しアクティビティを実行する際に、並列で
 同時実行できる!
  – すなわち、.NET 4.0 の Parallel.ForEach, ParallelEnumerable.AsParallel と同類

• これからのマルチコア、メニーコア時代へ向けての布石となる!
Demo
そのほか
はまったこと
• bool な依存プロパティをバインドできない!


• プロパティウィンドウの該当プロパティの欄に、[...] がないよ?


• 実はプロパティウィンドウの、プロパティ名横のアイコンダブルクリ
 ック、もしくはプロパティウィンドウ下部のタスクペインから [プロパ
 ティ 'XXX' のバインド(B)...] をクリックすることでいけた。
Demo
ナゾ
• すでに見てきたとおり、依存プロパティのバインドを設定するウィン
 ドウで、エラー言われる


• ...が、かまわず設定できるし、ちゃんと機能する。
参考サイト
• Channel9のビデオ "endpoint.tv Screencast"
  http://channel9.msdn.com/tags/WF+endpoint+screencasts/

いまさらながらの Windows Workflow 入門

  • 1.
    いまさらながらの WindowsWorkflow入門 ~そして実際にWFを実務に活用してみて思ったこと~ 株式会社アジャスト http://www.adjustgroup.co.jp
  • 2.
    Agenda • まずはコーディングして体を慣らします。 – Hands On • なぜ、WF のシーケンシャルワークフローを使うのか? • そのほか – はまったこと – ナゾ – 参考サイト
  • 3.
  • 4.
    開発者の視点では "ワークフロー" って何? •つまるところは .NET のクラス(ないしはそのインスタンス)です。 • Visual Studio のデザイナ画面でグラフィカルにデザインできますが、 究極的には、C#ソースコードを書いているに過ぎません。
  • 5.
    “アクティビティ” について • アクティビティ(Activity)= 活動、動作 • ワークフローを構成する最小要素のこと。 • これもつまるところ、.NET クラス(ないしはそのインスタンス)のこ とです。 • Activity クラスないしはその派生クラスになります。 • Visual Studio は大変賢く、”アクティビティ” クラスを適切にツールパ レットに表示します。
  • 6.
  • 7.
    • プロジェクトの新規作成 >Visual C# > Windows > WPF アプリケーション を作る。 • ソリューション右クリック > 新しいプロジェクトの追加 > Workflow > シー ケンシャルワークフローライブラリ を作る。 • WpfApplication1 で、WorkflowLibrary1 への参照設定を済ませる。 • ビルドを確認。 • デザイナ画面に Delay をDropしてDurationに3秒を設定。 • デザイナ画面に CodeActivity の ExecuteCode をハンドル、参照設定で PresentationCore と PresentationFramework を追加し、「 MessageBox.Show("OK");」をコーディング。 • ここまでやって、Workflow1.Designer.cs を開いてみる。
  • 8.
    WFを使うプログラムの構造 • WorkflowRuntime オブジェクトをnew して使う。 • ワークフローの実行は WorkflowRuntime オブジェクトが司る。 • 開発した"ワークフロー"クラスは、自分で new しちゃダメ。 • WorkflowRuntime オブジェクトに、開発した"ワークフロー"の Type を指定して、その"ワークフロー"をインスタンス化(new)してもらう。 • WorkflowRuntime オブジェクトから、new してもらったワークフロ ーオブジェクトを内包する、WorkflowInstance オブジェクトが返さ れるので、 その WorkflowInstance オブジェクトの Start メソッドで 、そのワークフローの実行が開始!
  • 9.
  • 10.
    • WpfApplication1 プロジェクトにて、 System.Workflow.Activities,ComponentModel,Runtime を参照設定追加。 • Window1.xaml のコードを表示、WorkflowRuntime の private メンバ変数 _wfRuntime を用意。 • コンストラクタ Windows1 にて WorkflowRuntime を new して _wfRuntime に設定。 • Window1.xaml のデザイナ画面にて、Window の Unloaded イベントをハン ドル、_wfRuntimeの StopRuntime メソッドを呼び出し。 • Window1.xaml のデザイナ画面にて、ボタンを1個配置、Clickイベントハンド ラを作成。 • Click ハンドラ内に、CreateWorkflow 呼び出しを記述、返ってきたワークフロ ーインスタンスに対して Start メソッド呼び出し。 • ビルドして実行。
  • 11.
    ワークフローは別スレッドで実行されます • なので、ワークフローオブジェクトの Startメソッドは、その呼び出 しからすぐに呼び出し元に返ります。 • 同期のコーディングが必要。 • WorkflowRuntime オブジェクトから、「ワークフローの実行が完遂 したよ」などのイベントが発行されますので(※ワークフローオブジ ェクトからではない)、予めイベントハンドラを設定しておいたりし ます。
  • 12.
  • 13.
    • Window1.cs のコード表示。 •コンストラクタ Window1 () にて、_wfRuntime の WorkflowCompleted イ ベントハンドラを追加。 • 同イベントハンドラ内で、MessageBox 呼び出しをコーディング。
  • 14.
    どうやって引数を渡すの? • "ワークフロー" のプロパティを介在します。 •"ワークフロー" を開発するときに、受け取りたい引数は、"ワークフロ ー" のプロパティとして実装します。 • 次にワークフローを呼び出すアプリ側では、渡したい引数を、 Dictionary<string, object> によるキーバリュー形式の辞書オブジェ クトにまとめます。 • この辞書オブジェクトを、WorkflowRuntime オブジェクトに "ワーク フロー" をインスタンス化(new)してもらうときに同時に、インスタン ス化メソッドの引数に渡します。 • すると、ワークフローオブジェクトの同名のプロパティに、辞書で渡 された値が設定されて、ワークフローを開始可能となります。
  • 15.
  • 16.
    • Workflow1.cs のコードを表示。 •Workflow1 クラスに string Message プロパティ追加。 • codeActivity1_ExecuteCode ハンドラ中の MessageBox.Show 呼び出しにて 、この Message プロパティを表示するように改造。 • Window1.xaml のコード表示。 • button1_Click にて CreateWorkflow 呼び出しの前に、辞書構築のコードを追 加。 • CreateWorkflow の第二引数に追加。 • ビルドして実行。
  • 17.
    カスタムアクティビティ • どうということはなく、自前で Activityクラスから派生したクラスで す。 • Execute 仮想メソッドをオーバーライドして、そこに自由にコードを 書きます。 • ビルドしたら、Visual Studio のツールボックスに表示されるようにな ります。 • ワークフローにDrug&Dropできます。
  • 18.
  • 19.
    • WorkflowLibrary1 プロジェクトにて右クリック> 追加 > アクティビティ > Activity1.cs を追加。 • デザイナ画面がひらいちゃうけどいったん閉じる。 • Activity1.cs のコード表示。 • 派生元を SequenceActivity からただの Activity に書き換え。 • Execute メソッドのオーバーライドを追加。 • 「MessageBox.Show("OK")」を記載。 • いったんビルド。 • Workflow1.cs のデザイナ表示。 • すべてのアクティビティをいったん削除。 • ツールパレット開くと、Activity1 が表示されているので、これをDrop。 • ビルドして実行。
  • 20.
    どうやって引数を渡すの? • カスタムアクティビティにプロパティを用意して、 これを介在します。 •しかし! • 普通にプロパティを実装するだけではダメ。 • 「依存プロパティ(Dependency Property)」というコーディングを行 う必要があります。 • こうしておくと、デザイナ画面にて、依存プロパティのバインド先が 指定できるようになります。 • こうして依存プロパティのバインドによって、引数を受け渡していき ます。
  • 21.
    依存プロパティのコーディング • DependencyProperty オブジェクトで"プロパティの定義" を作成。 – プロパティの名前(string) – プロパティの型(Type) – どのクラスに備わっているプロパティなのか(Type) • これを、static な public メンバ変数で公開。 • プロパティの実装コードでは、get, set それぞれに、Activityクラスの GetValue, SetValue メソッド呼び出しを記述。 • このときに、先に用意した "プロパティの定義"、すなわち、 DependencyProperty オブジェクトを、GetValue, SetValue メソッ ドの引数に渡す。 • ...というコードをスクラッチで書くのはありえないので、コードスニ ペット使います。
  • 22.
  • 23.
    • Activity1.cs のコード表示。 •コードスニペット使う。NetFX30 から。 • string な WhatToShow 依存プロパティを実装。なお、UIPropertyMetadata が余計なので削除。 • Exeute オーバーライドメソッド内の MessageBox 呼び出しにて、 WhatToShow 依存プロパティの値を表示するよう改訂。 • いったんビルド。 • Workflow1.cs のデザイナ表示。 • activity11 を選択すると、プロパティウィンドウに "WhatToShow" が増えて おり、[...] が押せる。 • [...] を押すとバインドのウィンドウが表示されるので、ここで、Workflow1 ク ラスの Message プロパティを選択する。 • ビルドして実行。
  • 24.
    依存プロパティについてもう少し... • 受け取るだけではなく、書き換えてもよいわけです。 • 後続するアクティビティは、書き換えられたプロパティ値を使うこと になります。 • ワークフローの"結果"、言うなれば、"戻り値" も、(ワークフローを 開始するときと同じように)プロパティ経由で受け取ります。 • WorkflowCompleted イベントの時点で、イベント引数 WorkflowCompletedEventArgs に、OutputParameters プロパティ という辞書の形で返ってきます。 • とにかく、プロパティを媒介として処理をリレーしていくイメージで す。
  • 25.
  • 26.
    • WorkflowLibrary1 プロジェクトにて右クリック> 追加 > アクティビティ > AddBlacketActivity.cs を追加。 • デザイナ画面がひらいちゃうけどいったん閉じる。 • AddBlacketActivity.cs のコード表示。 • 派生元を SequenceActivity からただの Activity に書き換え。 • string WhatToAddBlacket な依存プロパティ追加。 • Execute メソッドのオーバーライドを追加。 • 「WhatToAddBlacket = "[" + WhatToAddBlacket + "]"」をコーディング。 • いったんビルド。
  • 27.
    • Workflow1.cs のデザイナ表示。 •ツールボックスにAddBlacketActivity が増えているので、これを activity11 の 上に Drop。 • addBlacketActivity1 を選択し、プロパティウィンドウを開き、 WhatToAddBlacket 依存プロパティのバインドを指定、Workflow1.Message にバインド。 • ビルドして実行、メッセージが "[~]" で囲まれていることを確かめる。
  • 28.
    • Windows1.xaml のコード表示。 •_wfRuntime_WorkflowCompleted 内を var message = e.OutputParameters["Message"] as string; MessageBox.Show("Complete. Message is "+ message); に書き換え。 • ビルドして実行し、ワークフロー終了後のメッセージにて、、メッセージが "[ ~]" で囲まれていることを確かめる
  • 29.
  • 30.
    一般的に言われていること • アクティビティの組み合わせをデザイナ画面上でちょちょっと変える だけですむので、次のようなメリットがあります。 – 仕様変更に対する耐性が高くなる – カスタマイズ性能が向上する • プログラマではなく、エンドユーザー自身がプログラムの動作を変更 することができるようになる可能性が開けます。
  • 31.
    実際にやってみて、それ以上に思ったこと • "コードの可視化" というのは、並列処理化において強力であった! •「あ、このアクティビティとこのアクティビティは、依存関係がない から、同時に走らせていいや」ということに気づけるようになった! • そして、.NET 備え付けのアクティビティ=Parallel アクティビティを 組み合わせることで、デザイナ画面上で簡単に処理を並列化すること ができる! • さらに Replicator アクティビティを Parallel モードで使うと、配列な どの各要素に対して繰り返しアクティビティを実行する際に、並列で 同時実行できる! – すなわち、.NET 4.0 の Parallel.ForEach, ParallelEnumerable.AsParallel と同類 • これからのマルチコア、メニーコア時代へ向けての布石となる!
  • 32.
  • 33.
  • 34.
    はまったこと • bool な依存プロパティをバインドできない! •プロパティウィンドウの該当プロパティの欄に、[...] がないよ? • 実はプロパティウィンドウの、プロパティ名横のアイコンダブルクリ ック、もしくはプロパティウィンドウ下部のタスクペインから [プロパ ティ 'XXX' のバインド(B)...] をクリックすることでいけた。
  • 35.
  • 36.
  • 37.
    参考サイト • Channel9のビデオ "endpoint.tvScreencast" http://channel9.msdn.com/tags/WF+endpoint+screencasts/