Serialización de flujos de trabajo y actividades a y de XAML
Además de compilarse en tipos incluidos en ensamblados, las definiciones de flujo de trabajo también se pueden serializar en XAML. Estas definiciones serializadas se pueden recargar para edición o inspección, pasar a un sistema de compilación para compilación, o bien cargar e invocar. En este tema se proporciona información general sobre la serialización de definiciones de flujo de trabajo y el trabajo con definiciones de flujo de trabajo de XAML.
Trabajo con definiciones de flujo de trabajo de XAML
Para crear una definición de flujo de trabajo para la serialización, se utiliza la clase ActivityBuilder. La creación de una clase ActivityBuilder es muy similar a la creación de una clase DynamicActivity. Se especifican los argumento deseados y se configuran las actividades que constituyen el comportamiento. En el siguiente ejemplo, se crea una actividad Add
que toma dos argumentos de entrada, los suma y devuelve el resultado. Dado que esta actividad devuelve un resultado, se utiliza la clase ActivityBuilder<TResult> la genérica.
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")
}
}
};
Cada una de las instancias de DynamicActivityProperty representa uno de los argumentos de entrada al flujo de trabajo y la propiedad Implementation contiene las actividades que constituyen la lógica del flujo de trabajo. Observe que las expresiones de valor de r de este ejemplo son expresiones de Visual Basic. Las expresiones lambda no son serializables en XAML a menos que se use Convert. Si los flujos de trabajo serializados están pensados para abrirse o editarse en el diseñador de flujo de trabajo, se deben usar expresiones de Visual Basic. Para más información, consulte Creación de flujos de trabajo, actividades y expresiones mediante código imperativo.
Para serializar la definición de flujo de trabajo representada por la instancia de ActivityBuilder en XAML, use ActivityXamlServices para crear XamlWriter y use después XamlServices para serializar la definición de flujo de trabajo mediante XamlWriter. ActivityXamlServices tiene métodos para asignar instancias ActivityBuilder en XAML y viceversa, y cargar flujos de trabajo de XAML y devolver DynamicActivity que se puede invocar. En el ejemplo siguiente, la instancia ActivityBuilder del ejemplo anterior se serializa en una cadena y se guarda en un archivo.
// 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();
En el siguiente ejemplo se representa el flujo de trabajo serializado.
<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>
Para cargar un flujo de trabajo serializado, use el método ActivityXamlServices Load. Este método toma la definición de flujo de trabajo serializada y devuelve una clase DynamicActivity que representa la definición de flujo de trabajo. Observe que el XAML no se deserializa hasta que se llama al método CacheMetadata en el cuerpo de la clase DynamicActivity durante el proceso de validación. Si no se llama explícitamente a la validación, se realiza al invocar el flujo de trabajo. Si la definición de flujo de trabajo de XAML no es válida, se produce una excepción ArgumentException. Las excepciones que se producen desde el método CacheMetadata escapan de la llamada al método Validate y deben ser administradas por el autor de la llamada. En el siguiente ejemplo, el flujo de trabajo serializado del ejemplo anterior se carga y se invoca utilizando 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);
Cuando se invoca este flujo de trabajo, en la consola se muestra el siguiente resultado.
25 + 15
40
Nota
Para más información sobre cómo invocar flujos de trabajo con argumentos de entrada y salida, consulte Usar WorkflowInvoker y WorkflowApplication y Invoke.
Si el flujo de trabajo serializado contiene expresiones C#, una instancia ActivityXamlServicesSettings con su propiedad CompileExpressions establecida en true
se debe pasar como parámetro a ActivityXamlServices.Load; de lo contrario, se generará una NotSupportedException con un mensaje similar al siguiente: El tipo de actividad de expresión "CSharpValue`1" requiere la compilación para ejecutarse. Asegúrese de que el flujo de trabajo se ha compilado.
ActivityXamlServicesSettings settings = new ActivityXamlServicesSettings
{
CompileExpressions = true
};
DynamicActivity<int> wf = ActivityXamlServices.Load(new StringReader(serializedAB), settings) as DynamicActivity<int>;
Para más información, consulte Expresiones de C#.
Una definición de flujo de trabajo serializada también se puede cargar en una instancia de ActivityBuilder a través del método ActivityXamlServices CreateBuilderReader. Una vez que se carga un flujo de trabajo serializado en una instancia de ActivityBuilder, se puede inspeccionar y modificar. Esto resulta útil para los autores de diseñadores de flujo de trabajo personalizados y proporciona un mecanismo para guardar y recargar las definiciones de flujo de trabajo durante el proceso del diseño. En el siguiente ejemplo, se carga la definición de flujo de trabajo serializada del ejemplo anterior y se inspeccionan sus propiedades.
// 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);
}