動的な更新

動的更新は、ワークフロー アプリケーション開発者が永続化されたワークフロー インスタンスのワークフロー定義を更新するためのメカニズムを提供します。 これは、バグ修正、新しい要件の実装、または予期しない変更に対応することです。 このトピックでは、.NET Framework 4.5 で導入された動的更新機能の概要について説明します。

永続化されたワークフロー インスタンスに動的な更新を適用するために、必要な変更を反映するように永続化されたワークフロー インスタンスを変更する方法を説明するランタイムの指示を含む DynamicUpdateMap が作成されます。 更新マップが作成されると、必要な永続化されたワークフロー インスタンスに適用されます。 動的更新が適用されると、新しく更新されたワークフロー定義を使用してワークフロー インスタンスを再開できます。 更新マップを作成して適用するには、4 つの手順が必要です。

  1. 動的更新用にワークフロー定義を準備します。
  2. 必要な変更を反映するようにワークフロー定義を更新します。
  3. 更新マップを作成します
  4. 必要な永続化されたワークフロー インスタンスに更新マップを適用します

更新マップの作成に対応する手順 1 ~ 3 は、更新プログラムの適用とは別に実行できます。 一般的なシナリオは、ワークフロー開発者が更新マップをオフラインで作成し、管理者が後で更新プログラムを適用することです。

この記事では、コンパイル済み Xaml ワークフローの永続化されたインスタンスに新しいアクティビティを追加する動的更新プロセスの概要について説明します。

動的更新用のワークフロー定義を準備する

動的更新プロセスの最初の手順は、必要なワークフロー定義を更新用に準備することです。 これを行うには、 DynamicUpdateServices.PrepareForUpdate メソッドを呼び出し、変更するワークフロー定義を渡します。 このメソッドは、ワークフロー ツリーを検証し、後で変更されたワークフロー定義と比較できるように、タグ付けする必要があるパブリック アクティビティや変数などのすべてのオブジェクトを識別します。 これが完了すると、ワークフロー ツリーが複製され、元のワークフロー定義にアタッチされます。 更新マップが作成されると、更新されたバージョンのワークフロー定義が元のワークフロー定義と比較され、相違点に基づいて更新マップが生成されます。

動的更新用に Xaml ワークフローを準備するには、 ActivityBuilderに読み込まれ、 ActivityBuilderDynamicUpdateServices.PrepareForUpdateに渡されます。

シリアル化されたワークフローと ActivityBuilderの操作の詳細については、「XAML との間での ワークフローとアクティビティのシリアル化」を参照してください。

次の例では、(複数の子アクティビティを含むMortgageWorkflowで構成される) Sequence定義がActivityBuilderに読み込まれ、動的更新用に準備されます。 メソッドの実行が完了すると、ActivityBuilder には元のワークフロー定義とそのコピーが含まれます。

// Load the MortgageWorkflow definition from Xaml into
// an ActivityBuilder.
XamlXmlReaderSettings readerSettings = new XamlXmlReaderSettings()
{
    LocalAssembly = Assembly.GetExecutingAssembly()
};

XamlXmlReader xamlReader = new XamlXmlReader(@"C:\WorkflowDefinitions\MortgageWorkflow.xaml",
    readerSettings);

ActivityBuilder ab = XamlServices.Load(
    ActivityXamlServices.CreateBuilderReader(xamlReader)) as ActivityBuilder;

// Prepare the workflow definition for dynamic update.
DynamicUpdateServices.PrepareForUpdate(ab);

必要な変更を反映するようにワークフロー定義を更新する

ワークフロー定義が更新用に準備されたら、必要な変更を行うことができます。 アクティビティの追加または削除、パブリック変数の追加、移動または削除、引数の追加または削除、アクティビティ デリゲートのシグネチャの変更を行うことができます。 実行中のアクティビティを削除したり、実行中のデリゲートの署名を変更したりすることはできません。 これらの変更は、コードを使用するか、再ホストされたワークフロー デザイナーで行う場合があります。 次の例では、前の例のVerifyAppraisalの本文を構成するカスタム MortgageWorkflow アクティビティが Sequence に追加されています。

// Make desired changes to the definition. In this example, we are
// inserting a new VerifyAppraisal activity as the 3rd child of the root Sequence.
VerifyAppraisal va = new VerifyAppraisal
{
    Result = new VisualBasicReference<bool>("LoanCriteria")
};

// Get the Sequence that makes up the body of the workflow.
Sequence s = ab.Implementation as Sequence;

// Insert the new activity into the Sequence.
s.Activities.Insert(2, va);

更新マップを作成する

更新用に準備されたワークフロー定義が変更されたら、更新マップを作成できます。 動的更新マップを作成するために、 DynamicUpdateServices.CreateUpdateMap メソッドが呼び出されます。 これにより、新しいワークフロー定義を使用して読み込んで再開できるように、ランタイムが永続化されたワークフロー インスタンスを変更するために必要な情報を含む DynamicUpdateMap が返されます。 次の例では、前の例の変更された MortgageWorkflow 定義に対して動的マップが作成されます。

// Create the update map.
DynamicUpdateMap map = DynamicUpdateServices.CreateUpdateMap(ab);

この更新マップは、永続化されたワークフロー インスタンスの変更にすぐに使用できます。または、通常は保存し、後で更新プログラムを適用することができます。 更新マップを保存する方法の 1 つは、次の例に示すように、それをファイルにシリアル化することです。

// Serialize the update map to a file.
DataContractSerializer serializer = new DataContractSerializer(typeof(DynamicUpdateMap));
using (FileStream fs = System.IO.File.Open(@"C:\WorkflowDefinitions\MortgageWorkflow.map", FileMode.Create))
{
    serializer.WriteObject(fs, map);
}

DynamicUpdateServices.CreateUpdateMapが返されると、DynamicUpdateServices.PrepareForUpdateの呼び出しで追加された複製されたワークフロー定義とその他の動的更新情報が削除され、更新されたワークフロー インスタンスを再開するときに後で使用できるように、変更されたワークフロー定義を保存する準備が整います。 次の例では、変更されたワークフロー定義が MortgageWorkflow_v1.1.xamlに保存されます。

// Save the modified workflow definition.
StreamWriter sw = File.CreateText(@"C:\WorkflowDefinitions\MortgageWorkflow_v1.1.xaml");
XamlWriter xw = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(sw, new XamlSchemaContext()));
XamlServices.Save(xw, ab);
sw.Close();

必要な永続化されたワークフロー インスタンスに更新マップを適用する

更新マップの適用は、作成後いつでも行うことができます。 DynamicUpdateMapによって返されたDynamicUpdateServices.CreateUpdateMap インスタンスを使用してすぐに実行することも、後で更新マップの保存されたコピーを使用して行うこともできます。 ワークフロー インスタンスを更新するには、WorkflowApplicationInstance を使用して、そのワークフロー インスタンスを WorkflowApplication.GetInstance に読み込みます。 次に、更新されたワークフロー定義と目的のWorkflowApplicationを使用して、WorkflowIdentityを作成します。 この WorkflowIdentity は、元のワークフローの永続化に使用されたものとは異なる場合があり、通常は永続化されたインスタンスが変更されたことを反映するために使用されます。 WorkflowApplicationが作成されると、WorkflowApplication.Loadを受け取るDynamicUpdateMapのオーバーロードを使用して読み込まれた後、WorkflowApplication.Unloadの呼び出しでアンロードされます。 これにより、動的更新が適用され、更新されたワークフロー インスタンスが保持されます。

// Load the serialized update map.
DynamicUpdateMap map;
using (FileStream fs = File.Open(@"C:\WorkflowDefinitions\MortgageWorkflow.map", FileMode.Open))
{
    DataContractSerializer serializer = new DataContractSerializer(typeof(DynamicUpdateMap));
    object updateMap = serializer.ReadObject(fs);
    if (updateMap == null)
    {
        throw new ApplicationException("DynamicUpdateMap is null.");
    }

    map = (DynamicUpdateMap)updateMap;
}

// Retrieve a list of workflow instance ids that corresponds to the
// workflow instances to update. This step is the responsibility of
// the application developer.
List<Guid> ids = GetPersistedWorkflowIds();
foreach (Guid id in ids)
{
    // Get a proxy to the persisted workflow instance.
    SqlWorkflowInstanceStore store = new SqlWorkflowInstanceStore(connectionString);
    WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(id, store);

    // If desired, you can inspect the WorkflowIdentity of the instance
    // using the DefinitionIdentity property to determine whether to apply
    // the update.
    Console.WriteLine(instance.DefinitionIdentity);

    // Create a workflow application. You must specify the updated workflow definition, and
    // you may provide an updated WorkflowIdentity if desired to reflect the update.
    WorkflowIdentity identity = new WorkflowIdentity
    {
        Name = "MortgageWorkflow v1.1",
        Version = new Version(1, 1, 0, 0)
    };

    // Load the persisted workflow instance using the updated workflow definition
    // and with an updated WorkflowIdentity. In this example the MortgageWorkflow class
    // contains the updated definition.
    WorkflowApplication wfApp = new WorkflowApplication(new MortgageWorkflow(), identity);

    // Apply the dynamic update on the loaded instance.
    wfApp.Load(instance, map);

    // Unload the updated instance.
    wfApp.Unload();
}

更新されたワークフロー インスタンスを再開する

動的更新が適用されると、ワークフロー インスタンスが再開される可能性があります。 新しく更新された定義と WorkflowIdentity を使用する必要があることに注意してください。

WorkflowApplicationWorkflowIdentityの操作の詳細については、「WorkflowIdentity とバージョン管理の使用」を参照してください。

次の例では、前の例の MortgageWorkflow_v1.1.xaml ワークフローがコンパイルされ、更新されたワークフロー定義を使用して読み込まれ、再開されます。

// Load the persisted workflow instance using the updated workflow definition
// and updated WorkflowIdentity.
WorkflowIdentity identity = new WorkflowIdentity
{
    Name = "MortgageWorkflow v1.1",
    Version = new Version(1, 1, 0, 0)
};

WorkflowApplication wfApp = new WorkflowApplication(new MortgageWorkflow(), identity);

// Configure persistence and desired workflow event handlers.
// (Omitted for brevity.)
ConfigureWorkflowApplication(wfApp);

// Load the persisted workflow instance.
wfApp.Load(InstanceId);

// Resume the workflow.
// wfApp.ResumeBookmark(...);