将工作流和活动序列化为 XAML 和从 XAML 序列化工作流和活动
本主题适用于 Windows Workflow Foundation 4。
除了可将工作流定义编译为程序集中包含的类型之外,还可将工作流定义序列化为 XAML。这些已序列化的定义可以重新加载以供编辑或检测,可以传递给生成系统以供编译,也可以加载并调用。本主题概述如何序列化工作流定义以及如何使用 XAML 工作流定义。
使用 XAML 工作流定义
若要创建工作流定义以进行序列化,请使用 ActivityBuilder 类。ActivityBuilder 的创建方式与 DynamicActivity 的创建方式极其类似。指定任何所需的参数,并配置构成行为的活动。在下面的示例中,将要创建 Add
活动,此活动使用两个输入参数并将它们相加,然后返回结果。由于此活动返回一个结果,因此将使用泛型 ActivityBuilder 类。
ActivityBuilder<int> ab = new ActivityBuilder<int>();
ab.Name = "Add";
ab.Properties.Add(new DynamicActivityProperty { Name = "Operand1", Type = typeof(InArgument<int>) });
ab.Properties.Add(new DynamicActivityProperty { Name = "Operand2", Type = typeof(InArgument<int>) });
ab.Implementation = new Sequence
{
Activities =
{
new WriteLine
{
Text = new VisualBasicValue<string>
{
ExpressionText= "Operand1.ToString() + \" + \" + Operand2.ToString()"
},
},
new Assign<int>
{
To = new ArgumentReference<int> { ArgumentName = "Result" },
Value = new VisualBasicValue<int>
{
ExpressionText = "Operand1 + Operand2"
}
}
}
};
每个 DynamicActivityProperty 实例均表示工作流的一个输入参数,并且 Implementation 包含构成工作流逻辑的活动。
若要将 ActivityBuilder 实例表示的工作流定义序列化为 XAML,请使用 ActivityXamlServices 创建 XamlWriter,然后通过使用 XamlWriter 用 XamlServices 序列化工作流定义。ActivityXamlServices 具有一些方法,用于将 ActivityBuilder 实例映射到 XAML 和从 XAML 映射这些实例,加载 XAML 工作流以及返回可调用的 DynamicActivity。在下面的示例中,将上一示例中的 ActivityBuilder 实例序列化为一个字符串,并将其保存到一个文件中。
// Serialize the workflow to XAML and store it in a string.
StringBuilder sb = new StringBuilder();
StringWriter tw = new StringWriter(sb);
XamlWriter xw = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(tw, new XamlSchemaContext()));
XamlServices.Save(xw , ab);
string serializedAB = sb.ToString();
// Display the XAML to the console.
Console.WriteLine(serializedAB);
// Serialize the workflow to XAML and save it to a file.
StreamWriter sw = File.CreateText(@"C:\Workflows\add.xaml");
XamlWriter xw2 = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(sw, new XamlSchemaContext()));
XamlServices.Save(xw2, ab);
sw.Close();
下面的示例表示序列化的工作流。
<Activity
x:TypeArguments="x:Int32"
x:Class="Add"
xmlns="https://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml">
<x:Members>
<x:Property Name="Operand1" Type="InArgument(x:Int32)" />
<x:Property Name="Operand2" Type="InArgument(x:Int32)" />
</x:Members>
<Sequence>
<WriteLine Text="[Operand1.ToString() + " + " + Operand2.ToString()]" />
<Assign x:TypeArguments="x:Int32" Value="[Operand1 + Operand2]">
<Assign.To>
<OutArgument x:TypeArguments="x:Int32">
<ArgumentReference x:TypeArguments="x:Int32" ArgumentName="Result" />
</OutArgument>
</Assign.To>
</Assign>
</Sequence>
</Activity>
若要加载序列化的工作流,请使用 ActivityXamlServices Load 方法。这将使用序列化的工作流定义,并返回一个表示该工作流定义的 DynamicActivity。请注意,在验证过程中对 DynamicActivity 的正文调用 CacheMetadata 之前,不对 XAML 进行反序列化。如果未显式调用验证,则在调用工作流时执行验证。如果 XAML 工作流定义无效,则会引发 Argument 异常。从 CacheMetadata 引发的所有异常均不会导致调用 Validate,必须由调用方进行处理。在下面的示例中,将使用 WorkflowInvoker 加载并调用上一示例中的序列化的工作流。
// Load the workflow definition from XAML and invoke it.
DynamicActivity<int> wf = ActivityXamlServices.Load(new StringReader(serializedAB)) as DynamicActivity<int>;
Dictionary<string, object> wfParams = new Dictionary<string, object>
{
{ "Operand1", 25 },
{ "Operand2", 15 }
};
int result = WorkflowInvoker.Invoke(wf, wfParams);
Console.WriteLine(result);
调用该工作流时,会向控制台显示以下输出。
25 + 15
40
注意: |
---|
有关使用输入和输出参数调用工作流的更多信息,请参见使用 WorkflowInvoker 和 WorkflowApplication 和 Invoke。 |
也可以使用 ActivityXamlServices CreateBuilderReader 方法将序列化的工作流定义加载到 ActivityBuilder 实例中。在将序列化的工作流加载到 ActivityBuilder 实例中后,可以对其进行检查和修改。这对自定义工作流设计器作者很有用,并将提供用于在设计过程中保存和重新加载工作流定义的机制。在下面的示例中,将加载上一示例中的序列化的工作流定义并检查其属性。
// Create a new ActivityBuilder and initialize it using the serialized
// workflow definition.
ActivityBuilder<int> ab2 = XamlServices.Load(
ActivityXamlServices.CreateBuilderReader(
new XamlXmlReader(new StringReader(serializedAB)))) as ActivityBuilder<int>;
// Now you can continue working with the ActivityBuilder, inspect
// properties, etc...
Console.WriteLine("There are {0} arguments in the activity builder.", ab2.Properties.Count);