动态更新

动态更新为工作流应用程序开发人员提供了一种机制,用于更新持久化工作流实例的工作流定义。 这可以实施 bug 修复、新要求或适应意外更改。 本主题概述了 .NET Framework 4.5 中引入的动态更新功能。

若要对持久化工作流实例应用动态更新,将创建一个 DynamicUpdateMap 包含运行时的说明,说明如何修改持久化工作流实例以反映所需的更改。 创建更新映射后,它将应用于所需的持久化工作流实例。 应用动态更新后,可以使用新的更新工作流定义恢复工作流实例。 创建和应用更新映射需要执行四个步骤。

  1. 准备用于动态更新的工作流定义
  2. 更新工作流定义以反映所需的更改
  3. 创建更新映射
  4. 将更新映射应用于所需的持久化工作流实例

注释

步骤 1 到 3(涵盖更新映射的创建)可以独立于应用更新来执行。 常见方案是工作流开发人员将脱机创建更新映射,然后管理员稍后将应用更新。

本文概述了将新活动添加到已编译 Xaml 工作流的持久实例的动态更新过程。

准备用于动态更新的工作流定义

动态更新过程中的第一步是为更新准备所需的工作流定义。 这是通过调用 DynamicUpdateServices.PrepareForUpdate 方法并传入要修改的工作流定义来完成的。 此方法验证工作流树,然后遍查所有对象,例如需要标记的公共活动和变量,以便稍后可以将其与修改后的工作流定义进行比较。 完成此作后,将克隆工作流树并将其附加到原始工作流定义。 创建更新映射时,工作流定义的更新版本与原始工作流定义进行比较,并根据差异生成更新映射。

要为动态更新准备 Xaml 工作流,可将其加载到 ActivityBuilder 中,然后将 ActivityBuilder 传递到 DynamicUpdateServices.PrepareForUpdate

注释

有关使用序列化工作流和 ActivityBuilder 的详细信息,请参阅将工作流和活动序列化为 XAML 和从 XAML 序列化工作流和活动

在以下示例中,将一个由多个子活动组成的 MortgageWorkflow 定义加载到 Sequence 中,然后准备进行动态更新。 方法返回后, 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 序列中。

// 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);

此更新映射可以立即用于修改持久化工作流实例,或者通常情况下,可以保存它,并且稍后应用这些更新。 保存更新映射的一种方法是将其序列化为文件,如以下示例所示。

// 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 和 Versioning 的使用

在以下示例中,上一个示例中的工作流已被编译,并使用更新的工作流定义加载和继续执行。

// 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(...);