动态更新为工作流应用程序开发人员提供了一种机制,用于更新持久化工作流实例的工作流定义。 这可以实施 bug 修复、新要求或适应意外更改。 本主题概述了 .NET Framework 4.5 中引入的动态更新功能。
若要对持久化工作流实例应用动态更新,将创建一个 DynamicUpdateMap 包含运行时的说明,说明如何修改持久化工作流实例以反映所需的更改。 创建更新映射后,它将应用于所需的持久化工作流实例。 应用动态更新后,可以使用新的更新工作流定义恢复工作流实例。 创建和应用更新映射需要执行四个步骤。
注释
步骤 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 。
注释
有关与WorkflowApplication和WorkflowIdentity的协作的更多信息,请查阅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(...);