动态更新
动态更新为工作流应用程序开发人员提供了一种机制,可用于更新持久化工作流实例的工作流定义。 操作可以是实施 bug 修复、实施新的要求或适应意外的变化。 此主题概述了 .NET Framework 4.5 中引入的动态更新功能。
动态更新
为了将动态更新应用于持久化的工作流实例,会创建一个 DynamicUpdateMap,其中包含描述如何修改持久化工作流实例以反映所需更改的运行时说明。 更新映射一旦创建,即应用于所需的持久化工作流实例。 应用动态更新后,可能会使用新的更新工作流定义来恢复工作流实例。 创建和应用更新映射须执行四个步骤的操作。
注意
请注意,步骤 1 到 3 涵盖更新映射的创建,可不通过应用更新来执行。 一个常见的方案是工作流开发人员脱机创建更新映射,然后由管理员在以后应用更新。
本主题将一个新活动添加至已编译 Xaml 工作流的持久性实例中,借此提供对动态更新过程的概述。
准备工作流定义以便进行动态更新
动态更新过程的第一步是准备要更新的工作流定义。 这是通过调用 DynamicUpdateServices.PrepareForUpdate 方法并传入要修改的工作流定义来实现的。 方法将验证然后遍历工作流树,以标识所有对象(如需要标记的公共活动和变量),以便稍后将其与修改的工作流定义进行比较。 完成此操作后,将对工作流树进行克隆并将其附加到原始工作流定义。 创建更新映射时,将比较工作流定义的更新版本和原始工作流定义,并根据比较差异生成更新映射。
若要为动态更新准备 Xaml 工作流,可将其加载到 ActivityBuilder,然后 ActivityBuilder 将传入 DynamicUpdateServices.PrepareForUpdate。
注意
有关使用序列化工作流和 ActivityBuilder 的详细信息,请参阅将工作流和活动序列化为 XAML 和从 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
活动添加到 Sequence,后者构成前例中 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 和版本控制。
在以下示例中,前面示例中的 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(...);