Voy a construir un simple flujo de trabajo de tipo secuencial en donde explicaré algunas de las actividades built-in que vienen con WF. Como entorno de desarrollo voy a utilizar Visual Studio 2008.
El flujo de trabajo va a recibir como parámetro una lista de nombres que previamente debemos pasar de la siguiente manera: "WorkflowSample1 Daniel Eduardo Jorge Manuel Santiago" al ejecutar la aplicación. Para cada nombre, vamos a escribir un saludo. La idea es mostrar cómo pasar parámetros a un flujo de trabajo desde la aplicación que lo aloja y además entender para qué sirven las actividades CodeActivity y ReplicatorActivity.
CodeActivity: especificamos un método al que después CodeActivity llamará. Se intenta que el código del método que hayamos especificado sea liviano ya que CodeActivity bloquea la ejecución de la instancia del flujo de trabajo hasta que nuestro método termina de ejecutarse. Si queremos implementar lógica de negocio lo mejor es crear una actividad personalizada.
ReplicatorActivity: dada una colección de objetos que implemente IList, crea y ejecuta sólo una actividad hija tantas veces como objetos haya en la colección. Es similar a la sentencia for each. Tenemos eventos como ChildInitialized que se dispara después que una actividad hija es inicializada y ChildCompleted después que ReplicatorActivity termina de correr la instancia de una actividad hija.
Para aclarar todo esto, abrimos Visual Studio y creamos un nuevo proyecto de tipo Sequential Workflow Console Application al que llamaremos WorkflowSample1. Por defecto, Visual Studio nos deja en el diseñador visual de flujos de trabajo para Workflow1.cs. Si no es así, vamos al diseñador para Workflow1.cs.
Lo primero es arrastrar desde el Toolbox una instancia de la actividad Replicator y colocarla en la superficie de diseño (en donde dice "Drop Activities to create a Sequential Workflow"). Inmediatamente vamos a ver un signo de exclamación que nos indica que la actividad Replicator debe contener una actividad. Todas estas pistas visuales que el diseñador muestra las actividades las proveen creando una clase que derive de la clase ActivityValidator y algunas otras cosas más que en otro artículo escribiré.
[Haga clic en la imagen para ampliar]

Luego, dentro de la actividad Replicator (replicatorActivity1) que acabamos de agregar, agregamos una instancia de la actividad Code, quedando de la siguiente manera.
[Haga clic en la imagen para ampliar]

Ahora podemos observar que replicatorActivity1 no tiene más errores pero sí los tiene codeActivity1, y es que no hemos especificado el método que codeActivity1 tiene que llamar cuando comience su ejecución. Por ahora lo dejamos así.
Vamos a concentrarnos en replicatorActivity1. La aplicación que aloja el flujo de trabajo va a pasar la colección de nombres a una propiedad declarada a nivel de flujo de trabajo a la que replicatorActivity1 estará atada. Entonces para crear esa propiedad y a la vez decirle a replicatorActivity1 que se ate a esa propiedad, vamos a buscar la propiedad InitialChildData de replicatorActivity1 y vamos a hacer clic en el botón "…" que aparece cuando nos paramos sobre InitialChildData en el explorador de propiedades. Una vez hecho esto, nos va a aparecer un cuadro de diálogo llamado "Bind ‘InitialChildData’ to an activity’s property" y va a estar seleccionada la solapa "Bind to an existing member", pero como nosotros no tenemos el miembro (propiedad) ya creado, nos vamos a la solapa "Bind to a new member" que nos va a pedir un nombre de miembro y si queremos crearlo como campo (variable) o propiedad. Vamos a poner de nombre "Nombres" y dejamos seleccionado "Create Property" para crear el nuevo miembro como propiedad.
[Haga clic en la imagen para ampliar]

Bien. Con esto lo que conseguimos es que replicatorActivity1 pueda recorrer cada elemento que haya dentro de la propiedad Nombres que si nos fijamos Visual Studio fue suficientemente inteligente como para darse cuenta que el tipo de dato que da soporte a la propiedad tiene que ser de tipo ‘System.Collections.IList’.
[Haga clic en la imagen para ampliar]

Ahora, nos vamos a las propiedades de replicatorActivity1, seleccionamos para que se muestren los eventos y hacemos doble clic sobre ChildInitialized para crear un manejador de evento. En el manejador de evento que se acaba de crear, vamos a agregar el siguiente código.
private void replicatorActivity1_ChildInitialized(object sender, ReplicatorChildEventArgs e)
{
_Nombre = e.InstanceData.ToString();
}
Tenemos que recordar que ChildInitialized es disparado por cada elemento que haya en la colección Nombres. Luego que se dispare ChildInitialized, se va a ejecutar la actividad codeActivity1 que va a tener que mostrar el saludo para el nombre actual sobre el que replicatorActivity1 está parado, es por eso que en el método replicatorActivity1_ChildInitialized le asignamos a una variable llamada "_Nombre" que aún no hemos creado, el valor de la propiedad InstanceData que trae ReplicatorChildEventArgs (que nos da el elemento sobre el que replicatorActivity1 está parado). Declaramos la variable "_Nombre" de la siguiente manera:
private string _Nombre;
Nos vamos al diseñador visual de flujos de trabajo y para los eventos de codeActivity1 (que tiene uno solo) hacemos doble clic sobre ExecuteCode para crear un manejador de evento. Una vez creado el manejador, lo dejamos de la siguiente manera:
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine(string.Format("Hola {0}. Buenos días.", _Nombre));
}
Una vez que el método codeActivity1_ExecuteCode terminó de escribir la cadena a la salida de la consola, la actividad va a cerrar (terminar) y replicatorActivity1 seguirá con el próximo elemento en la lista y volverá a crear una nueva instancia de codeActivity1.
Por último, nos queda pasar los nombres desde la aplicación que aloja al flujo de trabajo a la instancia del flujo de trabajo. Nos vamos al código de Program.cs en donde vamos a ver que Visual Studio agregó código como para iniciar el runtime de flujos de trabajo e iniciar una instancia de Workflow1 (nuestro flujo de trabajo). Le vamos a hacer una modificación. La línea:
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowSample1.Workflow1));
La vamos a remplazar por las siguientes:
Dictionary<string, object> lParametros = new Dictionary<string, object>();
lParametros.Add("Nombres", args);
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(WorkflowSample1.Workflow1), lParametros);
El método CreateWorkflow de WorkflowRuntime tiene una sobrecarga que acepta un tipo que va a ser el tipo de flujo de trabajo que vamos a querer ejecutar, y un diccionario del tipo Dictionary<string, object> que va a tener como clave el nombre de la propiedad que intentamos establecer y como valor el valor que queremos establecer para esa propiedad. En este caso para la propiedad "Nombres" que se encuentra definida en el flujo de trabajo vamos a establecer el valor de "args" que son los argumentos que vienen cuando ejecutamos la aplicación consola.
Compilamos y ejecutamos de la siguiente manera: "WorkflowSample1 Daniel Eduardo Jorge Manuel Santiago". Si todo va bien, deberíamos ver lo siguiente:
[Haga clic en la imagen para ampliar]
