我正在为我们的大型工作流程实施更新流程,这些流程有可能持续数年甚至数十年。我正在接手以前的开发人员离开的地方,并且在花了一周时间试图让现有代码(主要基于 Microsoft 提供的示例)工作之后,我已经废弃它并构建了我自己的 WorkflowVersioning 实用程序。虽然我已经能够成功地将持久实例更新到新版本,但在测试中我发现在更新过程中以及在恢复受影响的工作流时,各种更改都可能导致异常。由于我们工作流程的复杂性,我相信还有更多工作要做,但目前还没有攻击计划。
我列出了我遇到的 2 个具体问题,以及我的代码的重要部分。如果我需要提供代码的其他部分,请告诉我。此外,如果您自己在类似情况下实现了 DynamicUpdate,或者知道资源、代码示例或其他可能有帮助的文档,我愿意为此提供任何帮助!作为 WF 的新手(以及一般的专业发展),这是一个相当不错的学习过程,我一直在努力寻找特定于 WF4.5 的资源。
背景
我们目前正在开发的 3 个主要业务流程由大型流程组成,每个流程都有多个子流程,它们本身是由许多自定义活动构建的。每个进程中的主要步骤(我们称之为“任务”)由一个 NativeActivity 以及几个拖到 Xaml 设计图面上的 CodeActivities 组成。(我也有让设计师表现的问题,但我会把它留到一个单独的问题上)。
具体问题
1) 对流程进行更改时,如果我添加或删除参数,我会在应用更新时遇到异常:
Exception thrown: 'System.Activities.DynamicUpdate.InstanceUpdateException' in System.Activities.dll ("In order for an implementation map to be directly applied to a workflow instance, the implementation map must indicate that there is no change to arguments. The implementation map indicates that arguments of the activity definition have changed."
根据 MSDN(不会让我发布第三个链接,我会在评论中添加它),您应该能够添加和删除参数:是否需要进一步的代码来解决这个问题?至少可以说,谷歌的结果很薄弱。但是,在分析生成的 DynamicUpdateMap 时,我发现它确实跟踪了 New 和 Old 参数,所以我假设一定有办法!
2)当我向主流程添加变量时,恢复任务时出现异常:
Message=Unable to locate the ICompiledExpressionRoot for compiled location 'flowData'. Make sure that the definition for the activity containing this expression has been compiled.
我能找到的唯一结果是这篇关于 IIS的帖子,但是当我只是从 Visual Studio 运行桌面应用程序时,为什么会出现这个错误呢?我似乎找不到任何其他结果来为我指明这个方向..
WorkflowVersioning 实用程序项目
免责声明:此代码大部分归功于 Matt Milner 的 WF4.5 复数课程,这是我能找到的关于该主题的为数不多的(有些最新的)资源之一。我很想看到更多动态更新过程的例子,但现在:
准备更新流
首先,我从主进程的(父工作流).xaml 中获得一个 ActivityBuilder:
private static ActivityBuilder GetBuilderFromFile(string sourcePath)
{
// Create the XamlXmlReaderSettings.
Console.WriteLine("Creating Xaml Reader...");
XamlXmlReaderSettings readerSettings = new XamlXmlReaderSettings()
{
LocalAssembly = Assembly.LoadFile(
Path.GetFullPath(assemblyVersionsPath))
};
XamlXmlReader xamlReader = new XamlXmlReader(sourcePath, readerSettings);
ActivityBuilder activity = XamlServices.Load(ActivityXamlServices.CreateBuilderReader(xamlReader)) as ActivityBuilder;
return activity;
}
然后我调用 PrepareForUpdate:
DynamicUpdateServices.PrepareForUpdate(activityBuilder);
然后使用现在添加的 DynamicUpdate 元数据将 ActivityBuilder 保存回文件。
private static void SaveBuilderToFile(ActivityBuilder builder, string filePath)
{
// Make sure the activity builder has the right information about the c# expressions (even though its visual basic)
VisualBasic.SetSettings(builder, VisualBasic.GetSettings(builder));
// Set c# as the language
System.Activities.Presentation.Expressions.ExpressionActivityEditor.SetExpressionActivityEditor(builder, "C#");
WorkflowViewState.SetViewStateManager(
builder.Implementation, WorkflowViewState.GetViewStateManager(builder.Implementation));
string fullPath = Path.GetFullPath(filePath);
using (FileStream file = File.Create(fullPath))
{
using (XmlWriter xmlWriter = XmlWriter.Create(file, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
using (XamlWriter xamlWriter = ActivityXamlServices.CreateBuilderWriter( new XamlXmlWriter(xmlWriter, new XamlSchemaContext())))
{
XamlServices.Save(xamlWriter, builder);
}
}
}
}
在执行更新准备过程后,我使用设计器对工作流程进行更改,或者通过添加/删除参数/变量/活动来修改 NativeActivities。
创建一个 DynamicUpdateMap
在对工作流程进行任何必要的更改后,我会生成一个动态更新图:
public static void CreateUpdateMap(string processToMap, string sourceActivityXaml, Version targetIdentityVersion)
{
ActivityBuilder builder = GetBuilderFromFile(sourceActivityXaml);
DynamicUpdateMap map = DynamicUpdateServices.CreateUpdateMap(builder);
// save the update map
string updateDirectory = @"..\..\UpdateMaps";
string fileName = Path.Combine(updateDirectory, processToMap + ".map");
// ToDo: If a .map already exists here, either delete or rename it, or prompt the user to decide. Otherwise it mixes with the new one.
using (FileStream fileStream = File.OpenWrite(fileName))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(DynamicUpdateMap));
serializer.WriteObject(fileStream, map);
}
// save builder back to source file, with the dynamicupdate info removed.
SaveBuilderToFile(builder, sourceActivityXaml);
}
将更新映射应用于受影响的实例
最后,我在持久性中遍历每个实例,并应用更新:
foreach (Guid id in GetIds())
{
// Get a proxy to the instance
WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(id, store);
Console.WriteLine("Inspecting: {0}", instance.DefinitionIdentity);
if (instance.DefinitionIdentity != null &&
instance.DefinitionIdentity.Name == processToUpdate.ToString() &&
instance.DefinitionIdentity.Version.Equals(previousVersion))
{
Tuple<WorkflowIdentity, DynamicUpdateMap> mapInfo = manager.GetMap(instance.DefinitionIdentity);
DynamicUpdateMap map = mapInfo.Item2;
WorkflowIdentity newIdentity = mapInfo.Item1;
// get the application, loading, and applying the map
//WorkflowApplication wfApp = GetWorkflowApplication( manager[newIdentity], instance, newIdentity, map);
Activity wf = manager[newIdentity];
WorkflowApplication wfApp = new WorkflowApplication(wf, newIdentity);
wfApp.Load(instance, map);
wfApp.Unload();
}
else
{
instance.Abandon();
Console.WriteLine("Instance {0} is not updatable", instance.InstanceId);
}
}