Serialisieren von Workflows und Aktivitäten in und aus XAML

Zusätzlich zur Kompilierung in Typen, die in Assemblys enthalten sind, können Workflowdefinitionen in XAML serialisiert werden. Diese serialisierten Definitionen können zur Bearbeitung oder Überprüfung erneut geladen werden, zur Kompilierung in ein Buildsystem übergeben werden oder geladen und aufgerufen werden. Dieses Thema bietet einen Überblick über das Serialisieren von Workflowdefinitionen und das Arbeiten mit XAML-Workflowdefinitionen.

Arbeiten mit XAML-Workflowdefinitionen

Zum Erstellen einer Workflowdefinition zur Serialisierung wird die ActivityBuilder-Klasse verwendet. Die Erstellung einer ActivityBuilder-Klasse funktioniert ähnlich wie die Erstellung einer DynamicActivity. Alle gewünschte Argumente werden angegeben, und die Aktivitäten, aus denen das Verhalten besteht, werden konfiguriert. Im folgenden Beispiel wird eine Add-Aktivität erstellt, die zwei Eingabeargumente verwendet, diese zusammenfügt und das Ergebnis zurückgibt. Da diese Aktivität ein Ergebnis zurückgibt, wird die generische ActivityBuilder<TResult>-Klasse verwendet.

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")
        }
    }
};

Die einzelnen DynamicActivityProperty-Instanzen stellen die Eingabeargumente für den Workflow dar, und das Implementation-Element enthält die Aktivitäten, aus denen die Logik des Workflows besteht. Beachten Sie, dass die Ausdrücke mit R-Wert in diesem Beispiel Visual Basic-Ausdrücke sind. Lambda-Ausdrücke sind nicht in XAML serialisierbar, es sei denn, Convert wird verwendet. Wenn die serialisierten Workflows im Workflow-Designer geöffnet oder bearbeitet werden sollen, sollten Visual Basic-Ausdrücke verwendet werden. Weitere Informationen finden Sie unter Erstellen von Workflows, Aktivitäten und Ausdrücken mit imperativem Code.

Um die von der ActivityBuilder-Instanz dargestellte Workflowdefinition in XAML zu serialisieren, verwenden Sie ActivityXamlServices, um XamlWriter zu erstellen, und anschließend XamlServices, um die Workflowdefinition mithilfe von XamlWriter zu serialisieren. ActivityXamlServices verfügt über Methoden, um Zuordnungen zwischen ActivityBuilder-Instanzen und XAML herzustellen und um XAML-Workflows zu laden und eine aufrufbare DynamicActivity zurückzugeben. Im folgenden Beispiel wird die ActivityBuilder-Instanz aus dem vorherigen Beispiel in eine Zeichenfolge serialisiert und in einer Datei gespeichert.

// 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();

Im folgenden Beispiel wird der serialisierte Workflow dargestellt.

<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>

Zum Laden eines serialisierten Workflows wird die ActivityXamlServicesLoad-Methode verwendet. Diese verwendet die serialisierte Workflowdefinition und gibt eine DynamicActivity zurück, die die Workflowdefinition darstellt. Beachten Sie, dass die XAML-Daten erst deserialisiert werden, wenn CacheMetadata während des Validierungsprozesses für den Text der DynamicActivity aufgerufen wird. Wenn die Validierung nicht explizit aufgerufen wird, erfolgt diese beim Aufrufen des Workflows. Wenn die XAML-Workflowdefinition ungültig ist, wird eine ArgumentException-Ausnahme ausgelöst. Alle von CacheMetadata ausgelösten Ausnahmen werden im Aufruf von Validate nicht verarbeitet und müssen vom Aufrufer behandelt werden. Im folgenden Beispiel wird der serialisierte Workflow aus dem vorherigen Beispiel geladen und von WorkflowInvoker aufgerufen.

// 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);

Wenn dieser Workflow aufgerufen wird, wird die folgende Ausgabe in der Konsole angezeigt.

25 + 15
40

Hinweis

Weitere Informationen zum Aufrufen von Workflows mit Ein- und Ausgabeargumenten finden Sie unter Verwenden von WorkflowInvoker und WorkflowApplication und Invoke.

Wenn der serialisierte Workflow C#-Ausdrücke enthält, muss eine ActivityXamlServicesSettings-Instanz mit der zugehörigen CompileExpressions-Eigenschaft, die auf true festgelegt ist, als Parameter an ActivityXamlServices.Load übergeben werden, da andernfalls eine NotSupportedException mit etwa folgender Meldung ausgelöst wird: Ausdrucksaktivitätstyp „CSharpValue'1“ erfordert eine Kompilierung, um ausgeführt zu werden. Stellen Sie sicher, dass der Workflow kompiliert wurde.

ActivityXamlServicesSettings settings = new ActivityXamlServicesSettings
{
    CompileExpressions = true
};

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

Weitere Informationen finden Sie unter Ausdrücke C# Expressions.

Eine serialisierte Workflowdefinition kann auch mit der ActivityXamlServicesCreateBuilderReader-Methode in eine ActivityBuilder-Instanz geladen werden. Nachdem ein serialisierter Workflow in eine ActivityBuilder-Instanz geladen wurde, kann er überprüft und geändert werden. Dies ist besonders nützlich für Autoren benutzerdefinierter Workflows, da es eine Methode zum Speichern und zum erneuten Laden von Workflowdefinitionen während des Entwurfsprozesses bereitstellt. Im folgenden Beispiel wird die serialisierte Workflowdefinition aus dem vorherigen Beispiel geladen, und ihre Eigenschaften werden überprüft.

// 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);
}