将工作流和活动序列化为 XAML 和从 XAML 序列化工作流和活动

除了可将工作流定义编译为程序集中包含的类型之外,还可将工作流定义序列化为 XAML。 这些已序列化的定义可以重新加载以供编辑或检测,可以传递给生成系统以供编译,也可以加载并调用。 本主题概述如何序列化工作流定义以及如何使用 XAML 工作流定义。

使用 XAML 工作流定义

若要创建工作流定义以进行序列化,请使用 ActivityBuilder 类。 ActivityBuilder 的创建方式与 DynamicActivity 的创建方式极其类似。 指定任何所需的参数,并配置构成行为的活动。 在下面的示例中,将要创建 Add 活动,此活动使用两个输入参数并将它们相加,然后返回结果。 由于此活动返回一个结果,因此将使用泛型 ActivityBuilder<TResult> 类。

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>("Operand1.ToString() + \" + \" + Operand2.ToString()")
        },
        new Assign<int>
        {
            To = new ArgumentReference<int> { ArgumentName = "Result" },
            Value = new VisualBasicValue<int>("Operand1 + Operand2")
        }
    }
};

每个 DynamicActivityProperty 实例均表示工作流的一个输入自变量,并且 Implementation 包含构成工作流逻辑的活动。 请注意,此示例中的右值表达式是 Visual Basic 表达式。 Lambda 表达式不可序列化为 XAML,除非使用 Convert。 如果序列化的工作流要在工作流设计器中打开或进行编辑,则应使用 Visual Basic 表达式。 有关详细信息,请参阅使用命令性代码创作工作流、活动和表达式

若要将由 ActivityBuilder 实例表示的工作流定义序列化为 XAML,应使用 ActivityXamlServices 创建 XamlWriter,然后通过 XamlServices,使用 XamlWriter 对工作流定义进行序列化。 ActivityXamlServices 提供一些方法,可以在 ActivityBuilder 实例和 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="http://schemas.microsoft.com/netfx/2009/xaml/activities"
  xmlns:x="http://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>

若要加载序列化的工作流,请使用 ActivityXamlServicesLoad 方法。 这将使用序列化的工作流定义,并返回一个表示该工作流定义的 DynamicActivity。 请注意,在验证过程中对 CacheMetadata 的正文调用 DynamicActivity 之前,不对 XAML 进行反序列化。 如果未显式调用验证,则在调用工作流时执行验证。 如果 XAML 工作流定义无效,则会引发 ArgumentException 异常。 从 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 和 WorkflowApplicationInvoke

如果序列化的工作流包含 C# 表达式,CompileExpressions 属性设置为 trueActivityXamlServicesSettings 实例必须作为参数传递给 ActivityXamlServices.Load,否则将引发 NotSupportedException,并显示类似于以下内容的消息:表达式活动类型“CSharpValue`1”需要编译才能运行。请确保已编译工作流。

ActivityXamlServicesSettings settings = new ActivityXamlServicesSettings
{
    CompileExpressions = true
};

DynamicActivity<int> wf = ActivityXamlServices.Load(new StringReader(serializedAB), settings) as DynamicActivity<int>;

有关详细信息,请参阅 C# 表达式

也可以使用 ActivityXamlServicesCreateBuilderReader 方法将序列化的工作流定义加载到 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);
foreach (var prop in ab2.Properties)
{
    Console.WriteLine("Name: {0}, Type: {1}", prop.Name, prop.Type);
}