SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Vista時代のプログラミングモデル .NET Framework 3.0入門

WF(Windows Workflow Foundation)チュートリアル 中編

Vista時代のプログラミングモデル .NET Framework 3.0入門 (6)

  • X ポスト
  • このエントリーをはてなブックマークに追加

ダウンロード サンプルソース (19.0 KB)

データ交換サービスインターフェースの実装

 次に、データ交換サービスインターフェースを実装しましょう。

 今回はホスティングアプリケーションであるStateMachineWorkflowSampleApplicationプロジェクトのForm1クラスで、先ほど定義したIOrderServiceを実装します。

 StateMachineWorkflowSampleApplicationプロジェクトで、[プロジェクト]-[参照の追加]から「StateMachineWorkflowLibrary」プロジェクトを追加します。

StateMachineWorkflowLibraryプロジェクトへの参照の追加
StateMachineWorkflowLibraryプロジェクトへの参照の追加

 同様に「System.Workflow.Activitiesアセンブリ」「System.Workflow.ComponentModelアセンブリ」「System.Workflow.Runtimeアセンブリ」への参照も追加します。

アセンブリへの参照の追加
アセンブリへの参照の追加

 Form1.csに以下のコードを追加してください。IOrderServiceインターフェースで定義した発送イベントOrderShippedと、状態更新メソッドItemStatusUpdateを実装しています。

IOrderServiceインターフェースの実装
public event EventHandler<OrderEventArgs> OrderShipped;

delegate void ItemStatusUpdateDelegate(Guid instanceId, 
                                       string message);

public void ItemStatusUpdate(Guid instanceId, string message)
{
  //Invokeメソッド呼び出しが必要な場合
  //(UIスレッド以外からの呼び出しの場合)
  if (this.InvokeRequired)
    {
      //delegateを介してInvoke呼び出しを行う
      //→UIスレッドからこのメソッドを呼び直す
      ItemStatusUpdateDelegate itemStatusUpdate
        = new ItemStatusUpdateDelegate(ItemStatusUpdate);
      object[] args = new object[2] { instanceId, message };
      this.Invoke(itemStatusUpdate, args);
    }
  else
    {
      //インスタンスIDとメッセージを表示
      MessageBox.Show(instanceId + ":" + message);
      //コンボボックスから完了したワークフローのインスタンスIDを削除
      this.comboBox1.Items.Remove(instanceId);
      if (this.comboBox1.Items.Count == 0)
        {
          this.comboBox1.Enabled = false;
        }
    }
}

 OrderShippedイベントはそのままとして、ItemStatusUpdateメソッド内でInvokeRequiredプロパティの値により分岐を行っているのを不思議に思われるかもしれません。

 ワークフロー側から呼び出されたItemStatusUpdateメソッドはUIスレッドで実行されていないため、直接UI上のコントロール(ここではコンボボックスなど)の値を変更することができません。そのため、Invokeメソッドを介してUIスレッドに処理を委譲しています。

 ItemStatusUpdateメソッドの実際の処理は、ワークフローから通知されたメッセージをダイアログ表示し、完了したワークフローのインスタンスIDをコンボボックスから削除する、というものです。

ワークフロー ランタイムへのデータ交換サービスの登録

 次に、定義したインターフェースを、ホスティングアプリケーションでワークフロー ランタイムに登録する必要があります。こちらもStateMachineWorkflowLibraryプロジェクトのForm1クラスで行います。コンストラクタ前後を以下のように書き換えてください。

ワークフロー ランタイムへのデータ交換サービス登録
//ワークフロー ランタイム
WorkflowRuntime workflowRuntime;

public Form1()
{
  InitializeComponent();
  //ワークフロー ランタイムの生成
  workflowRuntime = new WorkflowRuntime();
  //(1) データ交換サービス(ExternalDataExchangeService)の生成
  ExternalDataExchangeService dataService
    = new ExternalDataExchangeService();
  //(2) ワークフロー ランタイムへのデータ交換サービスの登録
  workflowRuntime.AddService(dataService);
  //(3) ExternalDataExchange属性を持つ自分自身を
  //    データ交換サービスに追加
  workflowRuntime.AddService(this);
  workflowRuntime.StartRuntime();
}

 データ交換サービスはワークフローを実行する前にワークフロー ランタイムに登録しておく必要があります。

 ExternalDataExchangeServiceクラスを生成し(1)、ワークフロー ランタイムにサービスを登録します(2)。System.Workflow.Activities.ExternalDataExchangeServiceは、WFの提供するデータ交換サービスクラスです。先ほど定義したIOrderServiceインターフェースの実装クラス(自分自身)をデータ交換サービスに登録します(3)。

 これにより、ワークフローに対してIOrderServiceインターフェースが紐付けられます。

 先ほどワークフローのデザインでIOrderServiceインターフェースのOrderShippedイベントを指定しましたが、このイベントはワークフロー ランタイムに登録されたデータ交換サービスを介して受け取ることになります。

ワークフローの起動

 では、いよいよワークフローを起動します。StateMachineWorkflowLibraryプロジェクトのForm1.csの[商品注文]ボタンをダブルクリックし、イベントハンドラに以下の内容を記述します。

ステートマシン ワークフローの実行
private void button1_Click(object sender, EventArgs e)
{
  //ワークフロー インスタンスを生成
  WorkflowInstance instance
    = workflowRuntime.CreateWorkflow(
           typeof(StateMachineWorkflowLibrary.Workflow1));
  //ワークフロー開始
  instance.Start();

  //InstanceIdをコンボボックスに追加
  this.comboBox1.Items.Add(instance.InstanceId);
  this.comboBox1.Enabled = true;
  this.comboBox1.SelectedIndex = 0;
}

 ワークフローの生成はシーケンシャル ワークフローの場合と同様に、ワークフロー ランタイムのCreateWorkflowメソッドを使います。

 ワークフロー開始後、インスタンスに自動的に割り当てられるインスタンスID(InstanceIdプロパティ)をコンボボックスに追加します。起動したワークフロー インスタンスをどこかに保存するのではなく、インスタンスIDだけ保存していることに注目してください。以降はこのインスタンスIDを使ってワークフローを指定することになります。

 なお、ここで起動したワークフローは、初期状態のWorkflow1InitialStateでイベント発生待ちになります。

イベントの送信

 次に、ワークフローへイベントを送信しましょう。[指定商品発送]ボタンをダブルクリックし、イベントハンドラに以下の内容を記述します。

ステートマシン ワークフローの実行
private void button2_Click(object sender, EventArgs e)
{
  //コンボボックスが選択されている場合は
  if (this.comboBox1.SelectedItem != null)
    {
      //発送イベント(OrderShipped)を発行する
      this.OrderShipped(null, 
              new OrderEventArgs((Guid)this.comboBox1.SelectedItem));
    }
}

 コンボボックスで、起動済のワークフローのインスタンスIDが選択されているかどうかをチェックし、OrderShippedイベントを発行します。

 ここで発行したOrderShippedイベントは、データ交換サービスを介してワークフローへ渡され、デザインしたとおりに状態遷移を発生させます。

サンプル実行

 では、[F5]キーを押して実行してみましょう。

 [商品注文]ボタンを押すとワークフローが起動し、コンボボックスにインスタンスIDが設定されます。

サンプル実行 ワークフロー起動
サンプル実行 ワークフロー起動

 [指定商品発送]ボタンを押すとイベントが送信され、ワークフローからItemStatusUpdateメソッドが呼び出され、結果がダイアログ表示されます。

サンプル実行 イベント送信結果
サンプル実行 イベント送信結果

 画面上に表示される内容はシンプルですが、背後でステートマシン ワークフローが動作しており、ホスティングアプリケーションからのイベント通知およびワークフローからのメソッド呼び出しがきちんと機能していることに注目してください。

まとめ

 非常にシンプルなパターンではありますが、これでステートマシン ワークフローの動作を確認することができました。ホスティングアプリケーション <-> ワークフロー間の相互呼び出しの仕組みであるデータ交換サービスは重要ですので、しっかり理解しておきましょう。

シーケンシャル ワークフローとデータ交換サービス
 データ交換サービスは、ステートマシン ワークフローだけではなく、シーケンシャル ワークフローでも同様に使用することができます。
 前回サンプルとして使ったような、「起動後は投げっぱなし」といったワークフローでは不要ですが、ホスティングアプリケーション側でワークフローの処理状況を表示したり、ユーザー入力などをワークフローに渡す場合には、シーケンシャル ワークフローにおいてもデータ交換サービスを活用することになります。

 今回のような簡易的なアプリケーションであれば、ワークフローを使う意義はあまりありません。しかし、より多くの状態を持つ、より複雑な業務を実装する場合には、すべてソースコードで実現するよりも、WFのステートマシン ワークフローを使うことで、ステート管理の煩雑さを解決し、可視化された分かりやすいプログラムとすることができます。

 さて、最終回となる後編では、永続化サービスなどWFの提供する便利な機能について触れ、このシリーズの結びとします。

参考資料

 ・@IT『InvokeRequiredプロパティについて

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Vista時代のプログラミングモデル .NET Framework 3.0入門連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 土井 毅(ドイ ツヨシ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/1651 2007/09/25 10:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング