[WF 4 (11)] ブックマーク

環境 : Visual Studio 2010 Beta 2 (.NET Framework 4)

WF 4

  1. そのアイデアとベースクラスの変更
  2. コードいらずのワークフロー (入門)
  3. ワークフローのコードと XAML の内部
  4. フローチャート (FlowCharts)
  5. アクティビティ (基礎)
  6. アクティビティコンテキスト (context) と変数 (Variable) の意義
  7. アクティビティにおけるさまざまな実行管理
  8. アクティビティのデリゲート
  9. アクティビティの非同期実行
  10. アクティビティデザイナー
  11. ブックマーク
  12. Workflow Extensions と 永続化、トラッキング
  13. デザイナーリホスティングとカスタムアプリケーション

こんにちは。

もたもたと書いているうちに、RC が登場し、Tech Days も近づいてきました。そろそろ終焉に向けて記載していきたいと思います。。。

今日は、ワークフローインスタンスと、外部連携についてです。

ワークフローでは、実行しているインスタンスに、その途中の段階で何か処理を要求したいケースが頻繁に発生します。例えば、フローを途中まで進めて、どこかの処理の段階でユーザーから入力を待ち、入力された内容によってフローを継続 (判断分岐など) するような場合です。
これまでの WF をご存じの方は痛いほど実感されていると思いますが、これまで、こうした頻繁に必要となる処理をおこなうために、「データ交換サービス」 (External Data Exchange Service) と呼ばれる非常に難解な動きを習得して使用する必要がありました。WF 4 では、新しく「ブックマーク」と呼ばれる概念を使用して、簡単に、実行中のインスタンスに要求を渡すことが可能になっています。

では、早速ソースコードをみていきましょう。(雑なソースですみません。。。)
下記では、WaitInputActivity というカスタムのアクティビティを作成し、この中で「ブックマーク」と呼ばれるものを作成して、外部からの入力を待機します。

public class WaitInputActivity : NativeActivity
{
    protected override bool CanInduceIdle
    {
        get
        {
            // Idle になる可能性があることを明示 !
            return true;
        }
    }

    protected override void Execute(NativeActivityContext context)
    {
        Console.WriteLine("Bookmark Start.");
        context.CreateBookmark("Bookmark1-MyWorkflow",
            new BookmarkCallback(OnResume));
    }

    void OnResume(NativeActivityContext context,
        Bookmark bookmark, object value)
    {
        Console.WriteLine("OnResume Called ! : {0}",
            value.ToString());
    }
}

. . .

static void Main(string[] args)
{
    AutoResetEvent t = new AutoResetEvent(false);

    WorkflowApplication app = new WorkflowApplication(new WaitInputActivity())
    {
        Completed = (e) =>
        {
            Console.WriteLine("Completed Called !");
            t.Set();
        }
    };

    app.Run();

    Console.WriteLine("Please input string !");
    app.ResumeBookmark("Bookmark1-MyWorkflow", Console.ReadLine());

    t.WaitOne();

    Console.ReadLine();
}

. . .

上記のようにアクティビティの中で Bookmark が作成されると、そのブックマークが外部から呼び出される (Resume される) まで、このアクティビティは Idle 状態になります。そして、ブックマークが外部から呼ばれると、ブックマーク用のコールバック (上記の場合は、OnResume メソッド) が呼び出され、処理が再開されます。

上記では、第 3 回でも使用した WorkflowApplication を使用していますが、 WorkflowApplication は、WorkflowInstance から継承されているオブジェクトで、要は、別スレッドとして実行されている「ワークフローインスタンス」そのものに対して、Main の中からブックマークの呼び出しをおこなっています。(これまで頻繁に使用していた WorkflowInvoker は、実は、内部でこの WorkflowApplication を使用しています。今回のように、インスタンスごとの処理が必要な場合には、WorkflowApplication を使う必要があります。)

ブックマークは複数作成することが可能ですが、上記の CreateBookmark メソッドからわかるように、ブックマークには「名前」を付けることが可能で、呼び出し側 (上記の Main) も、インスタンス (WorkflowApplication) に対して、この名前を使ってブックマークを呼び出しています。このため、ブックマークの名前は、インスタンス (ワークフロー) の中で一意になるように命名しておきましょう。

ご参考 : ワークフローサービス (WCF / WF 連携サービス) でも、Receive アクティビティのように、外部からの要求に応答する箇所で、このブックマークを使用しています。また、Delay アクティビティなどでも、(永続化されたインスタンスに対しても) タイマーで Wake Up ! できるように、内部でブックマークを使用しています。例えば、ワークフローサービスの Receive アクティビティでは、一意なブックマークの名前にするため、「サービス名」と「オペレーション名」を使用しています。(同一のワークフロー内に、同じサービスの同じオペレーションは複数存在しないため。)
ワークフローサービスについては、Tech Days の中でデモも含めご説明する予定です。

上記の実行結果は、以下の通りになります。

Please input string !
Bookmark Start.
Tsuyoshi
OnResume Called ! : Tsuyoshi
Completed Called !

上記は 1 回限りのブックマークですが、1 つのブックマークを繰り返し使用することもできます。
以下では、繰り返し呼び出し可能な OnContinue というコールバックを呼び出すブックマーク (Bookmark2 – MyWorkflow) を作成し、アクティビティが終了するまで何度も呼び出しています。(ただし、この場合、下記のように BookmarkOptions.NonBlocking を指定しないと、OnContinue が呼び出し可能な間じゅうずっとアクティビティが完了できなくなってしまうので注意してください。)

public class WaitInputActivity : NativeActivity
{
    protected override bool CanInduceIdle
    {
        get
        {
            // Idle になる可能性があることを明示 !
            return true;
        }
    }

    protected override void Execute(NativeActivityContext context)
    {
        Console.WriteLine("Bookmark Start.");
        context.CreateBookmark("Bookmark1-MyWorkflow",
            new BookmarkCallback(OnResume));
        context.CreateBookmark("Bookmark2-MyWorkflow",
            new BookmarkCallback(OnContinue),
            BookmarkOptions.MultipleResume | BookmarkOptions.NonBlocking);
    }

    void OnResume(NativeActivityContext context,
        Bookmark bookmark, object value)
    {
        Console.WriteLine("OnResume Called ! : {0}",
            value.ToString());
    }

    void OnContinue(NativeActivityContext context,
        Bookmark bookmark, object value)
    {
        Console.WriteLine("OnContinue Called ! : {0}",
            value.ToString());
    }
}

. . .

static void Main(string[] args)
{
    AutoResetEvent t = new AutoResetEvent(false);

    WorkflowApplication app = new WorkflowApplication(new WaitInputActivity())
    {
        Completed = (e) =>
        {
            Console.WriteLine("Completed Called !");
            t.Set();
        }
    };

    app.Run();

    // Bookmark2 を 9 回呼び出し、最後に Bookmark1 を呼び出して終了
    for (int i = 0; i < 10; i++)
    {
        if(i < 9)
            app.ResumeBookmark("Bookmark2-MyWorkflow", i);
        else
            app.ResumeBookmark("Bookmark1-MyWorkflow", i);
    }

    t.WaitOne();
}

. . .

実行結果 :

Bookmark Start.
OnContinue Called ! : 0
OnContinue Called ! : 1
OnContinue Called ! : 2
OnContinue Called ! : 3
OnContinue Called ! : 4
OnContinue Called ! : 5
OnContinue Called ! : 6
OnContinue Called ! : 7
OnContinue Called ! : 8
OnResume Called ! : 9
Completed Called !
すべて終了

繰り返しになりますが、かつてのデータ交換サービスと比べると非常に連携がおこないやすくなっていることがおわかり頂けるでしょう。

うーん、まだ記載すべきことが幾つか。。。
では、また次回 (少し後悔してきました。。。)

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s