Como comentaba Carlos Segura en su primer post, un workflow está compuesto por una serie de componentes, llamados actividades, que se enlazan de manera adecuada (y en función de estilo de creación del workflow, secuencial o máquina de estados) para constituir un modelo ejecutable de un cierto proceso de negocio. Como vemos en el diagrama de la arquitectura de WF, tenemos 5 elementos fundamentales:
· Un diseñador visual de workflows, para crear nuestros worflows en base a arrastrar actividades sobre una superficie por diseño. Por defecto el entorno natural para la creación de workflows es Visual Studio 2005 que actúa como hoster del diseñador visual de WF. Para tener disponible este diseñador, necesitamos las extensiones de WF para VS 2005. Como ya nos comentó Unai, el diseñador de WF se puede “sacar” del entorno de WF y hostearlo en otros entornos. Dos ejemplos de esto los tenemos con NetfxLive (un diseñador de workflows embebido en una aplicación web) o la aplicación WFPad.
· Una librería de actividades base, que son un conjunto de actividades que por defecto nos da WF para crear nuestros workflows. Algunos ejemplos de actividades son: IfElse, While, Policy, Delay, etc. Además de estas, ya tenemos actividades especificas para crear workflows en el entorno de WSSv3: SendEmail, OnTaskChange, OnWorkflowActivated, etc (estas actividades las tenéis a partir de la dll Microsoft.SharePoint.WorkflowActions.dll que tenéis que hay que añadir como referencia en nuestro proyecto de wrokflow para sharepoint y en en la toolbox de VS 2005), interactuar con servicios de WCF, y otros muchos ejemplos en el sitio oficial de WF .
· El runtime engine de WF, que es el responsable de la ejecución, control y gestión de las distintas instancias de workflows que tengamos creadas. Los componentes fundamentales son el motor de ejecución, los core services para el motor de ejecución (que se encargan de controlar la ejecución de los workflows), y el motor de reglas que permite utilizar reglas de negocio en nuestros workflows.
· Los servicios de runtime, que son piezas “enchufables” que permiten extender las capacidades del motor de ejecución de WF habilitando la posibilidad de persistir el estado de ejecución y los datos de un workflow, realizar un seguimiento (tracking) de las instancias del workflow en ejecución, comunicarlo con el exterior (aplicaciones locales y remotas), etc.
· Proceso de host, que se encarga de hospedar tanto el runtime engine como los runtime services, es decir, es el que en la práctica se encarga de instanciar y arrancar el motor de ejecución de WF, así como de instanciar los servicios de runtime necesarios. En este link podéis ver un estupendo resumen de todo lo que se puede hacer en el proceso de host de WF. Finalmente comentaros que la naturaleza del proceso de Host de WF es realmente variada, desde un servicio Windows o una aplicación de consola, pasando por un formulario Windows (justo el caso que veremos en este post) o una página ASP.NET, hasta entornos más complicados como WSSv3 o la BTS 2006 R2.
Después de este repaso arquitectónico, es momento de comenzar con los dos tópicos de este post: paso de datos a un workflow y uso de los servicios de runtime.
Paso de datos a un workflow
En el segundo post de su serie sobre WF, Carlos Segura ya nos introdujo uno de los mecanismos para pasar datos a un workflow: el paso de parámetros. La idea es explicaros un poco más en detalle cómo se implementa este paso de parámetros y que otras posibilidades tenemos para pasar información a nuestro workflow. Para ello, voy a partir de un sencillo workflow que calcula el factorial de un número y muestra un mensaje con el resultado.

Para pasar datos al workflow mediante el método de paso por parámetros, lo primero que tenemos que hacer es definir las propiedades necesarias en nuestro workflow. En nuestro caso vamos a pasar al workflow un único parámetro que vamos a pasar es el número cuyo factorial queremos calcular. Por lo tanto, en la vista de código de nuestro workflow sólo tendremos que añadir una propiedad que luego utilizaremos en la lógica que modela el comportamiento del workflow (a través de las actividades condicionales y las actividades code):
|
private string numero;
public string Numero
{
get { return numero; }
set { numero = value; }
} |
El siguiente paso es definir el paso de parámetros en el momento que se crea la instancia del workflow. Esto lo haremos en el hosting process que hayamos definido. En mi caso, he creado un simple formulario de Windows en el que en una caja de texto se recoge el valor numérico cuyo factorial queremos calcular. El código necesario para instanciar el runtime de WF, crear una instancia de nuestro workflow y pasar el parámetro sería el siguiente:
|
public partial class Form1 : Form
{
private WorkflowRuntime WR;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (WR==null)
{
WR = new WorkflowRuntime();
WR.StartRuntime();
}
Dictionary<string, object> parametros = new Dictionary<string, object>();
Parámetros.Add(“Numero”,textBox1.Text);
WorkflowInstance MiInstancia=WR.CreateWorkflow(typeof(Ejemplo_WF.Workflow1),parametros);
MiInstancia.Start();
}
private void Form1_FormClosed(object sender,FormClosedEventArgs e)
{
if (WR!=null)
{
if (WR.IsStarted)
{
WR.StopRuntime();
}
}
}
} |
Como vemos en el código anterior, los pasos para hostear el runtime engine de WF, crear una instancia de nuestro workflow y realizar el paso de parámetros son:
· Crear una instancia del runtime engine, y a continuación iniciarlo.
· Definir un diccionario de parámetros al que posteriormente le añadimos nuestro parámetro. Este diccionario lo hemos que definir de acuerdo a dos restricciones: (i) Los parámetros sólo pueden ser de tipo string y (ii) El nombre de los parámetros ha de coincidir con las propiedades que hayamos definido en nuestro workflow.
· Crear una instancia de nuestro workflow y realizar el paso de parámetros.
Comentaros que he incluido una función asociada a la acción de cerrar el formulario y en la que se para el runtime de WF en el caso de que decidamos cerrar el formulario. Por supuesto, la gestión del runtime de WF se puede hacer mucho mejor, aquí tenéis un ejemplo de un servicio Windows que actúa como hoster de WF y que incluye métodos para gestionar el inicio, parada o ejecución del workflow a partir de sobreescribir los métodos correspondientes. Finalmente, si probamos nuestro workflow con paso de parámetros, este sería el resultado:

¿Qué otras formas hay para pasar datos a un workflow? Además del paso de parámetros, WF define cuatro mecanismos adicionales:
|
 |
ü Mediante el uso de eventos a través de las actividades HandleExternalEvent y CallExternalMethod que habilitan respectivamente el paso de datos desde el exterior al workflow y desde el workflow al exterior.
ü Mediante la invocación de servicios web, a través de las actividades InvokeWebService, WebServiceInput y WebServiceOutput, que habilitan el paso de información en modo remoto bidireccional, entrante y saliente.
ü La misma idea que con la invocación de servicios web, pero invocando servicios WCF y utilizando para ellos las actividades ya disponibles en Codeplex.
|
ü Crear una actividad customizada que nos permita realizar el paso de datos.
ü Definir el paso de datos en una actividad de tipo code.
Añadiendo servicios al runtime
Añadir servicios al runtime de ejecución resulta bastante sencillo. En esta parte del post voy a explicar cómo se añade el servicio de tracking y que utilidad nos aporta en cuanto a información proporcionada. Cómo su nombre indica, este servicio permite monitorizar y realizar un seguimiento de los workflows en ejecución. Como se dice en el SDK de WF, este servicio permite que el proceso de host “observe” las instancias de workflows en tiempo de ejecución a partir de capturar distintos eventos que se lanzan durante dicha ejecución. Hasta aquí todo está claro, pero ¿qué hace el servicio de tracking con la información observada? La respuesta es depende del servicio de tracking que utilicemos. WF trae por defecto una implementación out-of-the-box para este servicio que escribe la información de tracking en una base de datos creada a priori. Por supuesto, podemos definir nuestros propios servicios de tracking customizados y almacenar la información de tracking en otros contenedores (otros gestores de BD, archivos XML, etc.).
En el ejemplo de este post, voy a utilizar el servicio de SQLTracking que por defecto trae WF. Para ello, y como paso previo, necesito establecer la infraestructura necesaria para el servicio, es decir, crear la BD SQL Server y los elementos necesarios para habilitar su uso. Como os podéis imaginar, .NET Framework 3.0 viene con los scripts SQL necesarios (ubicados en C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\EN):
· Tracking_Schema.sql, que crea la estructura de tablas necesaria para almacenar la información de tracking en la BD que especifiquemos.
· Logic_Schema.sql, que crea un conjunto de procedimientos almacenados necesarios para que el servicio de SQLTracking pueda almacenar la información de tracking, así como otros útiles para consultar dicha información.

Una vez creada la estructura necesaria para el servicio SQLTracking, para poder utilizarlo basta con añadir a nuestro servicio de host (en este caso un formulario Windows) las siguientes líneas:
|
SqlTrackingService ServicioTracking = new SqlTrackingService
("Data Source=MOSS2007;Initial Catalog=WF_Tracking;Integrated Security=True");
WR.AddService(ServicioTracking); |
Nota: Para poder utilizar el servicio de SQLTracking, tenemos que añadirlo al runtime de WF antes de iniciarlo.
Sin más, si ejecutamos nuestra aplicación y realizamos la siguiente consulta SQL en la BD de tracking creada, obtendremos información interesante relativa a las etapas de ejecución por las que ha pasado una o varias instancias de un workflow.
|
SELECT TrackingWorkflowEvent.Description as Fase_Ejecución,
WorkflowInstanceEvent.EventDateTime as Inicio_Fase,
WorkflowInstanceEvent.WorkflowInstanceInternalId as WorkflowId,
WorkflowInstance.WorkflowInstanceId as InstanciaWorkflow,
Type.TypeFullName as NombreWorkflow
FROM WorkflowInstanceEvent
INNER JOIN TrackingWorkflowEvent ON
WorkflowInstanceEvent.TrackingWorkflowEventId = TrackingWorkflowEvent.TrackingWorkflowEventId AND
WorkflowInstanceEvent.TrackingWorkflowEventId = TrackingWorkflowEvent.TrackingWorkflowEventId
INNER JOIN WorkflowInstance ON
WorkflowInstance.WorkflowInstanceInternalId=WorkflowInstanceEvent.WorkflowInstanceInternalId
INNER JOIN Type ON
Type.TypeId = WorkflowInstance.WorkflowTypeId
order by WorkflowInstanceEvent.WorkflowInstanceInternalId |
