[WF 4 (3)] ワークフローのコードと XAML の内部


環境 : 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. デザイナーリホスティングとカスタムアプリケーション

こんにちは。

前回の入門 では、簡単なワークフローの作成方法をみてきました。今回は、その内部のメカニズムをみていきましょう。

前回作成したワークフローは、XAML と呼ばれる宣言型のマークアップとして保存されています。これは、従来の WF (Windows Workflow Foundation) でも同じですが、使用される XAML の内容や、クラスの概念などもいろいろと異なっています。

まず、前回作成した Workflow1.xaml を右クリックして [コードの表示] を選択してみます。すると、以下の XML が表示されます。(これが、ワークフローのコードの実体です。)

<Activity mc:Ignorable="sap" x:Class="WorkflowConsoleApplicationTest.Workflow1"
          mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces"
          xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
          xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System"
          xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities"
          xmlns:s="clr-namespace:System;assembly=mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
          xmlns:s1="clr-namespace:System;assembly=mscorlib"
          xmlns:s2="clr-namespace:System;assembly=System"
          xmlns:s3="clr-namespace:System;assembly=System.Xml"
          xmlns:s4="clr-namespace:System;assembly=System.Core"
          xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities"
          xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation"
          xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System"
          xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel"
          xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.Core"
          xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=mscorlib"
          xmlns:sd="clr-namespace:System.Data;assembly=System.Data"
          xmlns:sd1="clr-namespace:System.Data;assembly=System.Data.DataSetExtensions"
          xmlns:sl="clr-namespace:System.Linq;assembly=System.Core"
          xmlns:st="clr-namespace:System.Text;assembly=mscorlib"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Sequence sad:XamlDebuggerXmlReader.FileName="C:\Demo\WorkflowConsoleApplicationTest\WorkflowConsoleApplicationTest\Workflow1.xaml" sap:VirtualizedContainerService.HintSize="266,350">
    <Sequence.Variables>
      <Variable x:TypeArguments="x:String" Name="DisplayValue" />
    </Sequence.Variables>
    <sap:WorkflowViewStateService.ViewState>
      <scg3:Dictionary x:TypeArguments="x:String, x:Object">
        <x:Boolean x:Key="IsExpanded">True</x:Boolean>
      </scg3:Dictionary>
    </sap:WorkflowViewStateService.ViewState>
    <Assign sap:VirtualizedContainerService.HintSize="244,60">
      <Assign.To>
        <OutArgument x:TypeArguments="x:String">[DisplayValue]</OutArgument>
      </Assign.To>
      <Assign.Value>
        <InArgument x:TypeArguments="x:String">["Ken"]</InArgument>
      </Assign.Value>
    </Assign>
    <WriteLine sap:VirtualizedContainerService.HintSize="244,64" Text="[&quot;My name is &quot; + DisplayValue]" />
    <Delay Duration="00:00:10" sap:VirtualizedContainerService.HintSize="244,22" />
  </Sequence>
</Activity>

ここで留意すべきは、上記の通り (3 番目 の要素の <Sequence.Variables /> 参照)、ワークフロー変数 (Variable) も XAML 内に宣言型で記述されているという点です。さらに、WriteLine で設定した "My name is " + DisplayValue という VB 式も XAML 内に記述されているのがわかります。(これらは、従来の WF には存在しない仕組みでした。)

また、このコンソールアプリケーションの Program.cs を開くと、Main には以下の通り記述されています。

. . .
using System.Activities;
using System.Activities.Statements;
. . .

static void Main(string[] args)
{
    WorkflowInvoker.Invoke(new Workflow1());
}

上述した Workflow1.xaml (XML のマークアップ) は、コンパイルによって、アセンブリ (.exe) 内のリソースとして埋め込まれ、上記のプログラムのように WorkflowInvoker クラスの Invoke メソッドによって、この XAML 内の Workflow1 クラスがロードされます。

WF 4 でワークフローを実行するには、ここで使用している WorkflowInvoker の他に、WorkflowApplication クラスを使用することもできます。WorkflowInvoker クラスは同期実行をおこないますが (つまり、ワークフローが終了するまで、上記の Invoke メソッドで処理が継続状態になります)、WorkflowApplication では非同期実行がおこなわれます。(ただし、WorkflowInvoker を使用した同期実行の場合でも、内部では、ワークフローインスタンスとして別のスレッドが作成されているので注意してください。)
WorkflowApplication クラスを使用して、上記の処理を以下の通り変更することもできます。

. . .
using System.Activities;
using System.Activities.Statements;
using System.Threading;
. . .

static void Main(string[] args)
{
    AutoResetEvent t = new AutoResetEvent(false);
    WorkflowApplication app = new WorkflowApplication(new Workflow1());
    app.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
    {
        t.Set();
    };
    app.Run();
    t.WaitOne();
}

補足 : 厳密には、WorkflowApplication は、ワークフローインスタンス (起動されて実行される個々のワークフロー) を継承しているラッパーで、WorkflowInvoker は、内部でこの WorkflowApplication を使用してインスタンスを開始しています。 

また、従来の WF 同様、XAML を使用せず、ワークフローをプログラムコードだけで記述することも可能です。
例えば、XAML を使用しない以下のコンソールアプリケーションは、上記の XAML を使用したワークフロー (前回構築したワークフロー) と等価な処理になります。(Argument や Variable のメカニズムについては、また別の回でちゃんとご説明します。)

. . .
using System.Activities;
using System.Activities.Statements;
using System.Activities.Expressions;
. . .

static void Main(string[] args)
{
    Variable v1 = new Variable<string>("DisplayName");
    Activity workflow1 = new Sequence()
    {
        Variables = { v1 },
        Activities =
        {
            new Assign()
            {
                To = new OutArgument<string>(new VariableReference<string>()
                {
                    Variable = v1
                }),
                Value = new InArgument<string>("Ken")
            },

            new WriteLine()
            {
                Text = new InArgument<string>((context) => "My name is " + v1.Get(context))
            },

            new Delay()
            {
                Duration = new InArgument<TimeSpan>(new TimeSpan(0, 0, 10))
            }
        }
    };

    WorkflowInvoker.Invoke(workflow1);
}

こうしてコードで記述され、メモリ中にロードされたワークフローオブジェクトと、前述した XAML の間では、シリアライズ / デシリアライズ (逆シリアライズ) が可能であるため、以下の通り記述すると、このワークフローオブジェクト (メモリ中のオブジェクト) のワークフロー定義を再び XAML にシリアライズ (ファイル保存、など) することができます。(注意 ! ただし、シリアライズ可能にするため、式として記述している値は VisualBasicValue、VisualBasicReference を使用する必要があります。このため、以下の太字部分を変更してください。)

. . .
using System.Activities;
using System.Activities.Statements;
using System.Activities.Expressions;
using Microsoft.VisualBasic.Activities;
. . .

static void Main(string[] args)
{
    Variable v1 = new Variable<string>("DisplayName");
    Activity workflow1 = new Sequence()
    {
        Variables = { v1 },
        Activities =
        {
            new Assign()
            {
                To = new OutArgument<string>(new VariableReference<string>()
                {
                    Variable = v1
                }),
                Value = new InArgument<string>("Ken")
            },

            new WriteLine()
            {
                Text = new InArgument<string>(new VisualBasicValue<string>{ ExpressionText="\"My name is \" + DisplayName" })
            },

            new Delay()
            {
                Duration = new InArgument<TimeSpan>(new TimeSpan(0, 0, 10))
            }
        }
    };

    WorkflowInvoker.Invoke(workflow1);

    System.Xaml.XamlServices.Save("test.xaml", workflow1);
}

WF を既にご存じの方には言うまでもありませんが、これは、オブジェクトの状態が永続化されているのではなく、"ワークフローの定義" を保存しています。(実行中のワークフローの永続化については、また別の回でご説明します。。。まだまだこのシリーズは続きそうです。。。)

このように、WF 4 では、基本的な概念は従来の WF を踏襲していますが、その中身は "モデル駆動" を強く意識したコード (実装) になっていることがおわかり頂けるでしょう。

 

Comments (0)

Skip to main content