将工作流和活动序列化为 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,然后通过使用 XamlWriterXamlServices 序列化工作流定义。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() + &quot; + &quot; + 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
Ff458319.note(zh-cn,VS.100).gif注意:
有关使用输入和输出参数调用工作流的更多信息,请参见使用 WorkflowInvoker 和 WorkflowApplicationInvoke

也可以使用 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);