MOSS 2007 – Refrito

Creo que a esto se le llama refrito, pero como algunos me estáis pidiendo información relativa a estos temas aquí dejo los links agrupados.


2006-11-28 Servicios Compartidos en MOSS2007

2006-10-07 MOSS 2007_Catalogo de datos empresariales BDC (6)
2006-10-04 MOSS 2007_Catalogo de datos empresariales BDC (5)
2006-10-02 MOSS 2007_Catalogo de datos empresariales BDC (4)
2006-10-01 MOSS 2007_Catalogo de datos empresariales BDC (3)
2006-09-29 MOSS 2007_Catalogo de datos empresariales BDC (2)
2006-09-23 MOSS 2007_Catalogo de datos empresariales BDC (1)

2006-09-23 WSS 3 – Tipos de Contenido (Content Types) (5)
2006-09-15 WSS 3 – Tipos de Contenido (Content Types) (4)
2006-09-13 WSS 3 – Tipos de Contenido (Content Types) (3)
2006-09-13 WSS 3 – Tipos de Contenido (Content Types) (2)
2006-09-05 WSS 3 – Tipos de Contenido (Content Types) (1)

2006-09-01 Los Eventos en WSS 3

2006-08-25 Las WebParts en WSS 3 y MOSS 2007_(4)
2006-08-24 Las WebParts en WSS 3 y MOSS 2007_(3)
2006-08-23 Las WebParts en WSS 3 y MOSS 2007_(2)
2006-08-23 Las WebParts en WSS 3 y MOSS 2007_(1)


2006-12-09 Acciones de flujos de trabajo personalizadas en SharePoint Designer (2)
2006-12-08 Acciones de flujos de trabajo personalizadas en SharePoint Designer (1)

2006-06-04 SharePoint – Sharepoint designer workflows

Refactoring – Session








  Yesterday was the third act of NavarraDotNet, we had a session in the Public University of Navarra, where, we spoke about refactoring techniques and showing some tools as Resharper and Refactor!.

My colleague in the session Sergio Jimenez, it demonstrated how using some of this inexpensive tools, we can improve the code quality, the development of some tasks, and how is it favorable for both, programmers and companies.

Ayer tuvimos el tercer acto de NavarraDotNet, en la Universidad Publica de Navarra, en donde hablamos sobre técnicas de refactoring y pudimos ver algunas herramientas como Resharper y Refactor!.


Mi colega en la sesión, Sergio Jimenez, nos demostró como usando estas herramientas, no excesivamente caras, podemos mejorar la calidad del código y del desarrollo de algunas tareas cotidianas, también hizo una reflexión sobre lo beneficioso del uso de estas herramientas, tanto para programadores cómo para sus empresas.


Si, yo llevaba la camiseta de Pearl Jam (Tour 2000) 🙂

   

Curso de Workflow Foundation (5)

5o Asalto – El servicio de planificación de tareas & Hosting in ASP.Net


El servicio de planificación de tareas (WorkflowSchedulerService)  es el encargado de ejecutar los distintos flujos de trabajo dentro del motor, estos pueden ser manejados de forma asíncrona, usando el servicio que WF nos proveed por defecto (DefaultWorkflowSchedulerService) o de manera síncrona, usando un servicio manual (ManualWorkflowSchedulerService).


Al igual que el resto de servicios, también podemos crear nuestro propio servicio de planificación heredando de WorkflowSchedulerService.


Es importante recalcar que el motor de WF no crea hilos de trabajo de su propiedad. Los hilos sobre los cuales se ejecutan las instancias de los flujos de trabajo, son hilos de la aplicación que hospeda el motor.  De modo que cada instancia (aunque esta sea del mismo flujo de trabajo) puede estar ejecutándose sobre diferentes hilos. Y como no, el encargado de gestionar esos hilos es WorkflowSchedulerService.


El servicio por defecto DefaultWorkflowSechedulerService,  no hemos de indicarlo ya que si el motor se inicia sin que nosotros añadamos un servicio de programación manual ó personalizado, usará este servicio.  Este servicio gestiona los hilos de ejecución de manera asíncrona de modo que podemos tener múltiples instancias de los flujos de trabajo en la cola de ejecución. 


Por el contrario, el servicio manual que nos provee WF, ManualWorkflowSchedulerService, usa una gestión síncrona,  de modo que las instancias de los flujos de trabajo serán ejecutadas sobre el mismo hilo de la aplicación huésped, bloqueando su ejecución hasta que la instancia quede inactiva.


No pensaba realizar ningún ejemplo en asp.net, pero me pico el otro día un par de mails, que recibí, de modo que me dieron las tantas otra vez. Nada mejor que un ejemplo para ver estas cosas.


En el ejemplo que he preparado (advertencia, es necesario que tengáis instalado AJAX 1.0) se puede apreciar como en un principio usando el DefaultWorkflowSchedulerService, se asignan más hilos que con el servicio manual, y como al usar el servicio manual ManualWorkflowSchedulerService, la ejecución de la aplicación queda detenida hasta que el flujo de trabajo queda inactivo.


Para activar el servicio manual, solo hay que quitar los comentarios tanto en el global.asax, como en el default.aspx.cs.



  AspHostWorkflow.zip (24,83 KB)

Curso de Workflow Foundation (4)

Muchos de los procesos con los que trabajamos habitualmente, requieren de datos o eventos que llegan desde el exterior. Anteriormente, vimos como WF puede comunicarse con el mundo exterior, para recibir esos datos o eventos usando el servicio de intercambio de datos.


En ocasiones, al igual que sucede en el mundo real, debemos esperar a que se produzcan esos datos o eventos para poder continuar realizando un trabajo. Este hecho hace que nuestros flujos de trabajo queden inactivos (Idle) mientras esperan esos datos.


Que se produzcan estos hechos para continuar el trabajo, puede llevar horas, días o incluso meses, de modo que nuestros flujos de trabajo se mantendrían dentro del motor de flujos de trabajo, consumiendo recursos de la aplicación que alberga el motor de WF, y a su vez quedarían expuestos a posibles pérdidas si durante el tiempo de espera el sistema se viene abajo.


Con el fin de subsanar esto WF, nos provee de un servicio de Persistencia, a través del cual nuestros flujos de trabajo, podrán ser guardados en un sistema de almacenamiento hasta el momento en que se produzcan los datos o eventos necesarios para continuar.


4º ASALTO – El servicio de persistencia


Desde el momento que se crea una instancia del flujo de trabajo del motor de WF, este queda en memoria. Hemos vistos a través de los ejemplos anteriores como los flujos de trabajo entran en inactividad cuando esperan datos externos o un evento que proviene del servicio de timer. También, hemos visto como se establece un horario en que deberían llegar esos datos y como este puede ser indeterminado cuando se establece como 31/12/2999.


Si deseamos persistir, nuestros flujos de trabajo, lo que hemos de hacer es proveer a nuestro motor de flujos de trabajo de un servicio de persistencia, esto es una operación muy similar a la que realizamos anteriormente con el servicio de intercambio de datos.


Cuando nuestro motor de flujos de trabajo cuenta con un servicio de persistencia, podremos descargar los flujos de trabajo en el sistema de almacenamiento mediante dicho servicio.


La clase base de los servicios de persistencia es la clase abstracta WorkflowPersistenceService, como vimos en el 3er Asalto, que a su vez hereda de WorkflowRuntimeService y WF nos ofrece una implementación de este servicio en la clase SqlWorkflowPersistenceService, que se encargará de almacenar los flujos de trabajo en un repositorio de SQL server.


Para poder usar SqlWorkflowPersistenceService, debemos crear una bbdd SQL Server con la estructura que se encuentra en C:WINDOWSMicrosoft.NETFrameworkv3.0Windows Workflow FoundationSQLESSqlPersistenceService_Schema.sql y la lógica (procedimientos almacenados) que se encuentra en el mismo directorio en el archivo SqlPersistenceService_Logic.sql


Podéis encontrar más detalles en el blog de Juan Carlos González


Una vez creada la base de datos, lo que debemos hacer es dotar a nuestro motor de flujos de trabajo del servicio de persistencia.

persistenceService =
new MySqlWorkflowPersistenceService(
Settings.Default.WorkflowDemoConnectionString,
UnloadOnIdle,
new TimeSpan(0, 1, 0),
new TimeSpan(0, 0, 10));
workflowRuntime.AddService(persistenceService);

En el constructor de SqlPersistenceService, debemos pasar la cadena de conexión a la base de datos donde se almacenarán los flujos de trabajo así como un flag indicando si los flujos de trabajo se descargarán automáticamente cuando se encuentren en inactividad.


En caso afirmativo, WF descargará los flujos de trabajo automáticamente:
–          Cuando se encuentren inactivos
–          Tras terminar una actividad marcada como PersistOnClosed
–          Antes de que el flujo de trabajo termine
–          Antes de que el flujo de trabajo se complete


En caso contrario, la responsabilidad pasará a ser de nuestro programa y deberemos cargar y descargar los flujos de trabajo manualmente, llamando a WorkflowInstance.Load(), WorkflowInstance.Unload() y WorkflowInstance.TryUnload().


De los otros dos parámetros del constructor, el primero nos permite indicar el tiempo que un flujo de trabajo almacenado por este motor mantendrá bloqueado dicho flujo de trabajo. Esto es para ser usado en caso de que distintos motores de flujos de trabajo compartan el mismo sistema de almacenamiento. El segundo, es el tiempo que se tardará en revisar los flujos de trabajo que han sido cargados desde la base de datos.


La mejor forma de ver cómo funciona el servicio de persistencia, es a través de la aplicación que estamos usando de ejemplo, la he actualizado incluyendo el servicio de persistencia, que podremos activar y desactivar a nuestro antojo. También hay una nueva pestaña en donde podemos monitorizar que es lo que ocurre en la base de datos donde se están almacenando los flujos de trabajo.





Podéis hacer pruebas a tirar la aplicación mientras hay flujos de trabajo persistidos y a volverla a ejecutar y activar el servicio de persistencia, veréis como los flujos de trabajo que quedaban pendientes se completan.  (Para ello debéis arrancar el motor manualmente tras activar el servicio de persistencia). La mejor forma de ver cómo funciona el servicio de persistencia, es a través del ejemplo adjunto.


Para ver cómo trabaja SqlPersistenceService, lo que he hecho es crear una subclase espejo que irá dejando rastro de cómo son llamados lo métodos de SqlPersistenceService, podéis ver esta información en VS a través de la vista output.

PersistenceService – Start
The thread 0xac4 has exited with code 0 (0x0).
PersistenceService – OnStarted
Created
Started
+PersistenceService – SaveWorkflowInstanceState WorkflowDelay Executing
-PersistenceService – SaveWorkflowInstanceState WorkflowDelay Executing
Idled
+PersistenceService – LoadWorkflowInstanteState 0fc8af43-ceb5-4f31-be58-c844e019531d
-PersistenceService – LoadWorkflowInstanteState 0fc8af43-ceb5-4f31-be58-c844e019531d
Loaded
Persisted
Unloaded
+PersistenceService – SaveWorkflowInstanceState WorkflowDelay Closed
-PersistenceService – SaveWorkflowInstanceState WorkflowDelay Closed
Persisted
+PersistenceService – LoadWorkflowInstanteState 0fc8af43-ceb5-4f31-be58-c844e019531d
+PersistenceService – LoadWorkflowInstanteState 0fc8af43-ceb5-4f31-be58-c844e019531d
Completed

Como ya comente con anterioridad, podemos crear nuestro propio servicio de persistencia, heredando de WorkflowPersistenceService. (En los ejemplos del WF hay un servicio personalizado que guarda los flujos de trabajo en archivos binarios FilePersistenceService.cs)


Básicamente hay que sobrescribir cuatro métodos de la clase WorkflowPersistenceService estos son:
SaveWorkflowInstanceState – Encargado de slavar la instancia del flujo de trabajo en el sistema de almacenamiento.
SaveCompletedContextActivity – Salva el estado ámbito (actividades) completado.
LoadWorkflowInstanceState – Carga desde el sistema de almacenamiento a memorial instancia del flujo de trabajo.
LoadCompletedContextActivity – Carga el ámbito  (actividades) completado


Por el momento, «jugar» con este servicio del que volveremos hablar más adelante.


 WinHostWorkflow_4.zip (474,63 KB)

SharePoint – csegSearch update 1.5

This is a small update of my webpart csegSearch (for SPS 2003) that solves some issues with the custom results. Now are correctly displayed and you can short this.


For more info see:




 csegSearchWebPart15.zip (12,7 KB)


Prior to update this release, you need uninstall previous versions of csegSearch.

Curso de Workflow Foundation (3a) – source code





El proyecto WinHostWorkflow esta formado a su vez por dos proyectos, WinHostWorkflow contiene el motor de flujos de trabajo (WorkflowEngine.cs) y el interface de usuario (WorkflowSampleApp.cs),  la carpeta ExternalDataServices, contiene los servicios de intercambio de datos del 3er asalto, (CalculatorService.cs y MessengerService.cs)

El proyecto Workflows, contiene los interfaces para los servicios de intercambio de datos (ICalculator.cs y IMessenger.cs) así como los flujos de trabajo de ejemplo.

WorkflowDelay.cs – Secuencial con una actividad Delay de 45seg.
WorkflowDelay2.cs – Secuencial con una actividad Delay de 30 seg, una actividad Code que duerme el proceso un minuto y una actividad Delay de 30 seg.
WorkflowLongWhile.cs – Seuencial con una actividad While que ejecuta una actividad Code.
WorkflowSumNumbers.cs – Uso de parámetros, recibe dos parámetros y los suma en una actividad Code.
WorkflowCallExternalMethod.cs – Llamada a métodos externos, usa el interface ICalculator, para  realizar operaciones con parámetros.
WorkflowHandleExternalEvent.cs – Espera recibir un mensaje, interface IMessenger, después hace una pausa y a través del mismo interface nos lo devuelve.

 WinHostWorkflow_3.zip (51,45 KB)

 

Curso de Workflow Foundation (3)

Cuando hablo de Workflow Foundation, siempre hay una parte en la que alguien me pregunta, ¿Cómo encajo todo esto en mi forma de trabajar?, bien, WF lo que nos permite es modelar procesos (pueden ser de negocio o de lo que sea).
 
Generalmente, y desde que adoptamos los modelos de n-capas, usamos una capa para albergar lo que denominamos lógica del negocio, (yo la llamo la capa lógica, a secas) y en esa capa implementamos los distintos procesos que nuestra aplicación va a realizar. En esta capa tenemos un nivel de abstracción elevado, ya que no trabajamos directamente con los datos, si no que usamos entidades que mapean los datos. A pesar de trabajar con este nivel de abstracción, en nuestro código debemos implementar gran cantidad de servicios, como: excepciones, estados, eventos, comunicaciones, transacciones. WF, lo que nos brinda es otro nivel más de abstracción mediante el cual vamos a poder centrarnos en la resolución de los problemas, liberándonos de gran parte de la carga.

¿Vamos a diseñarlo todo con flujos de trabajo?, no, claro que no. Pero hay gran cantidad de procesos que vamos a poder modelar mediante flujos de trabajo. WF, nos va a permitir implementar esos modelos, de modo que parte de esa lógica quede también desacoplada.


3er ASALTO – Comunicaciones


Después de esta pequeña introducción vamos a ver como se comunican los flujos de trabajo con el mundo exterior. Anteriormente vimos como un flujo de trabajo puede recibir parámetros, evidentemente estos han de conocerse con antelación y las cosas no siempre son así. Para poder comunicarnos con los flujos de trabajos debemos en primer lugar añadirle un servicio de intercambio de datos a nuestro motor (Runtime). Este servicio que va a ser el primero que veamos es el ExternalDataExchangeService.


WF nos ofrece unos servicios básicos con los que podemos empezar a trabajar, es decir nos ofrece implementaciones ya preparadas. Además esta capa se servicios que podemos conectar y desconectar del Runtime a nuestro antojo, no ofrece la posibilidad de personalizar éstos servicios, bastará con que heredemos de cualquiera de las clases padre de las implementaciones disponibles y sobrescribamos aquello que deseamos personalizar. El esquema de la capa de servicios es el siguiente: Todos los servicios se basan en la clase abstracta WorkflowRuntimeService,  Tracking, CommitWorkBatch, Loader, Persistence, Scheduler y ExternalDataExchange. Estos a excepción del ExternalDataExchangeService, son a su vez clases abstractas con al menos una implementación lista para usar.







[ExternalDataExchange]
public interface ICalculator
{
int Add(int numberA, int numberB);
int Subtract(int numberA, int numberB);
int Multiply(int numberA, int numberB);
int Divide(int numberA, int numberB);
void DisplayResult(int result);
}



ExternalDataExchangeService dataExchangeService;
dataExchangeService = new ExternalDataExchangeService();
workflowRuntime.AddService(dataExchangeService);



CalculatorService calculatorService;
calculatorService = new CalculatorService();
dataExchangeService.AddService(calculatorService);
El servicio ExternalDataExchangeService, nos va a permitir comunicarnos con los flujos de trabajo que viven en el interior del motor. Lo primero que debemos hacer es definir el interface que tendrá nuestro servicio, para comenzar vamos a usar una actividad en nuestros flujos de trabajo que se llama CallExternalMethod, esta actividad nos va a permitir comunicarnos desde dentro de un flujo de trabajo con el mundo exterior.

Dicho interface, define los métodos a los que se podrá invocar desde dentro del flujo de trabajo, este interface tiene que estar decorado con el atributo [ExternalDataExchange]. A la izquierda tenéis un ejemplo del interface que usaremos, sobran las explicaciones sobre lo que hace. Y obviamente, debemos implementar este interface. El código esta en el proyecto adjunto.

Por último, solo nos queda añadir el servicio de intercambio de datos al Runtime, son tres líneas. Recordar que cuando vimos la clase WorkflowRuntime, no solo podíamos añadir  si no que también podíamos quitar servicios. Una vez que hemos añadido el servicio, lo que necesitamos es añadir al Servicio de interambio de datos, nuestro propio servicio que implementa el interface que hemos definido.

ExternalDataExchangeService, a diferencia del resto de servicios, se ha diseñado como un contenedor de, podríamos llamar sub-servicios. Estos serán todos aquellos que necesitemos para intercambiar datos. Este contenedor también es conectable y des conectable, de modo que podemos añadir y quitar servicios a nuestro antojo.

Veamos como se conectar nuestro servicio, al servicio de intercambio de datos. Ops otras tres lineas …

Bien, ahora pasamos al diseñador de flujos de trabajo en donde, como he dicho utilizaremos la actividad CallEnternalMethodActivity para invocar cualquiera de los métodos que hemos definido en la interface, esto es importante ya que para el diseño del flujo de trabajo, solo nos es necesario contar con el interface. Este interface, puede estar (como es el caso del ejemplo) dentro de nuestro ensamblado con los flujos de trabajo, o en otro sitio, en cuyo caso deberemos añadirlo como una referencia. Lo mismo ocurre con la implementación del interface.


De modo que colocamos la actividad en el diseñador y lo primero que nos aparece como siempre es el símbolo de advertencia, indicando que  debemos completar el tipo de interface y el método dentro de este interface al que queremos llamar. Disponemos de un asistente para esto.


    


Una vez que seleccionemos el interface y el método al que queremos llamar, las propiedades cambiarán presentándonos los parámetros que requiere dicho método así como el tipo que devuelve. Los primeros aparecerán incluso con el mismo nombre que figura en el interface, el resultado aparecerá como (ReturnValue). Bien, disponemos de otro asistente para enlazar tanto los parámetros como el valor devuelto con propiedades de nuestro flujo de trabajo. (obviamente debemos crear las propiedades dentro de la clase que contiene el flujo de trabajo).


       


Una vez realizado esto, ya tenemos un flujo de trabajo que se comunicará con el exterior, de modo que podemos usarlo para recuperar valores desde el flujo de trabajo ó para realizar tareas en el exterior. En el ejemplo del día, tenéis un nuevo flujo de trabajo llamado CallExternalMethod (WorkflowCallExternalMethod.cs), con este ejemplo. La entrada de valores para realizar la suma se hace mediante los parámetros, al igual que hacíamos en el ejemplo anterior. Cuando veáis este ejemplo por favor volver a ver el flujo de trabajo SumNumbers (WorkflowSumNumbers.cs).


Bien, antes he mencionado que los parámetros como es lógico debemos conocerlos de antemano. Que ocurre si necesitamos datos dentro de nuestro flujo de trabajo una vez iniciado, ó si necesitamos que pasen cosas en el exterior para que nuestro flujo de trabajo pueda continuar. Esto es lo que vamos a ver ahora, y para ello vamos ha usar la actividad HandleExternalEvent. Por si alguien no lo ha pillado, si necesitamos datos debemos pedirlos (10 INPUT A), esto es lo que haremos con esta actividad.


Pero antes debemos introducir algunos cambios en el interface de comunicaciones.

[ExternalDataExchange]
public interface IMessenger
{
event EventHandler<MessageServiceEventArgs> MessageService;
}

[Serializable]
public class MessageServiceEventArgs : ExternalDataEventArgs
{
private string message;

public MessageServiceEventArgs(Guid instanceId, string mensaje) : base(instanceId)
{
this.message = mensaje;
WaitForIdle = true;
}

public string Message
{
get { return message; }
set { message = value; }
}
}


Lo primero es que debemos definir el evento que haremos llegar desde el exterior a nuestro flujo de trabajo, así como los argumentos que le serán pasados. La clase con los argumentos, debe ser serializable, es importante y hay que tenerlo en cuenta cuando estemos pasando objetos como argumentos a nuestros flujos de trabajo, ya que estos objetos deberán ser también serializables  (esto tiene q ver con la persistencia, lo veremos). Otra cosa importante es que el primer parámetro debe ser de tipo Guid, ya que cuando lancemos el evento, debemos indicarle a que instancia del flujo de trabajo va dirigido.

public class MessengerService : IMessenger
{
public void SendMessage(MessengerEventArgs args)
{
EventHandler<MessengerEventArgs> eventHandler = MessageService;

if (eventHandler != null)
eventHandler(null, args);
}

public event EventHandler<MessengerEventArgs> MessageService;
}


Por último al igual que hemos hecho anteriormente debemos implementar la clase que llevará a cabo las acciones. Nota, no esta reñido el hecho de que un mismo interface de comunicaciones tengamos eventos y métodos, esto lo he hecho así para clarificar.


HandleExternalEvent, se configura en el editor exactamente igual que como hemos hecho con CallExternalMethod. Se enlaza el tipo de interface y el evento en este caso que esperamos. Para recoger los datos una vez llegue el evento, debemos rellenar la propiedad (Invoked) de la actividad en el diseñador de VS, con el método de nuestro flujo de trabajo que será invocado cuando llegue el evento. En este método lo que haremos será castear el tipo para recoger los argumentos que nos han sido pasados.

 private void ExternalMessenger_Invoked(object sender, ExternalDataEventArgs e)
{
message = ((MessengerEventArgs)e).Message;
}

Este flujo de trabajo se llama en el ejemplo adjunto HandleExternalEvent (WorkflowHandleExternalEvent.cs). Una par de cosas más para terminar esta parte. La primera, como hacemos llegar el mensaje a nuestro flujo de trabajo, bien, para esto hemos de llamar al método SendMessage de  nuestro servicio MessengerService. Durante la llamada hemos de pasarle el intanceId del flujo de trabajo al que queremos enviar el mensaje así como el mensaje que queremos pasarle. Para ello, en el código adjunto y lo que hago es solicitar al Runtime el servicio y a través de este enviar el mensaje.

MessengerService messengerService = (MessengerService) WorkflowEngine.GetRunTime().GetService(typeof(MessengerService));
messengerService.SendMessage(new MessengerEventArgs(GetSelectedWorkflowInstance().InstanceId, «This is a sample message»));

Obviamente, podemos hacer esto de diversas maneras en función de como se haya implementado el motor y como se hayan añadido los servicios.


La última cosa, es que mientras el flujo de trabajo, está esperando la llegada del evento, este se pone en Idle (inactivo), podemos comprobar el timer que tomará el valor 31/12/9999 23:59:59, es decir que no espera ningún mensaje del timer, sin embargo el flujo de trabajo se encuentra esperando, inactivo, bien, podemos comprobar mediante GetWorkflowQueueData, que es lo que espera recibir este evento en su cola de datos. He añadido esta funcionalidad para que veáis claramente que es lo que pasa. Ver GetNextEvent() en el código. Para enviarle un mensaje, podéis hacerlo con el menu contextual seleccionando la instancia que espera recibir un mensaje.


Aplicación de ejemplo – 3


Nueva remodelación, hay más cosas. Espero seguir manteniendo vuestra atención sobre WF y no sobre el programa Winforms, si no es así lo decís. En el menú contextual he añadido una opción que nos mostrará el diagrama del flujo de trabajo. El código para elaborar el diagrama viene en uno de los ejemplos del SDK, lo he añadido y retocado para que veáis lo fácil que resulta añadirlo a nuestras aplicaciones.

Revisar WorkflowEngine.cs para ver como he añadido los servicios al motor. Bueno, solo deciros que hay mucho más, probar a elaborar vuestros propios flujos de trabajo, ahora tenéis muchas piezas para ensayar, os sugiero algunos ejercicios simples:


– Probar la actividad IfElse mandando el tipo de operación a realizar Suma, Resta, etc … realizar la operación en función de este.
– Pasar el resultado del ejemplo WorkflowSumNumbers.cs como entrada a otro flujo de trabajo.
– Ver que pasa cuando se ejecutan muchos flujos de trabajo. ¿se mantiene la precisión del timer? ¿cómo se carga la CPU?





Espero vuestros comentarios…

Curso de Workflow Foundation (2)

Bueno, en la primera parte hemos visto en que consiste el motor de flujos de trabajo, poco a poco nos iremos introduciendo en los detalles, también hemos visto que un flujo de trabajo esta compuesto de actividades. Cuando ejecutamos un flujo de trabajo dentro del motor, la representación del mismo (SequentialWorkflowActivity ó  StateMachineWorkflowActivity) en ejecución es un WorkflowInstance.

 






Esta instancia, se crea desde el motor, Runtime.CreateWorkflow(typeof(<nuestro flujo de trabajo>)) y como vimos existen distintas formas de cargar los flujos de trabajo dentro del mismo, (más adelante veremos como hacerlo de otras maneras).

A través de esta WorkflowInstance, podemos manejar las instancias de los flujos de trabajo, generalmente las iniciaremos nada más crearlas WorkflowInstance.Start(), como vimos en el ejemplo anterior usando el Id de la instancia (InstanceId), podemos controlar independientemente cada uno de los flujos de trabajo que esta ejecutando nuestro motor de flujos de trabajo.

Para ello disponemos de Start, que echará a andar el flujo de trabajo dentro del motor. Suspend, que detendrá su ejecución y que podremos continuar usando Resume.

Los métodos Load, Unload y TryUnload, podremos usarlos cuando nuestro motor tenga implementado un servicio de Persistencia. El servicio de persistencia, nos va ha permitir que nuestros flujos de trabajo se almacenen en un sistema de almacenamiento (ya se q queda redundante). Esto, podemos realizarlo de forma manual invocando Unload ó TryUnload pero será el propio motor quien se encargue de estas tareas cuando nuestro flujo de trabajo este inactivo a la espera de datos ó eventos para continuar.  Esto también lo veremos  más adelante (4º Asalto)

El método Terminate, terminará el flujo de trabajo, una vez terminado no podremos volver ha ejecutarlo. Cuando invocamos este método debemos pasar una cadena indicando un mensaje de error, este mensaje podremos recuperarlo en el evento WorkflowTerminated del motor usando WorkflowTerminatedEventArgs. Existe una actividad cuyo comportamiento es similar, llamada TerminateActivity, que que podremos usar dentro de nuestros flujos de trabajo para terminar un flujo de trabajo lanzando una excepción, esta actividad podría ser usada en el caso de que nuestro flujo de trabajo llegará a un punto en donde no puede continuar.

También podemos abortar el flujo de trabajo mediante Abort, que deshará todo lo que el flujo de trabajo ha realizado desde que este se guardo – persistió, (si se hizo).

Otra de las cosas que vimos en el ejemplo es el uso de GetWorkflowNextTimerExpiration, por medio del cual podemos ver cuando espera recibir nuestro flujo de trabajo un evento del servicio timer. En el ejemplo que suministre anteriormente teníamos un flujo de trabajo sencillo que incluía una actividad Delay, esta actividad DelayActivity detiene el flujo de trabajo durante el tiempo que le hayamos indicado por lo que establece un timer expiration. 
Tenemos un método similar que usaremos enseguida (6º Asalto) llamado, GetWorkflowQueueData, mediante el cual podemos ver que actividades están pendientes de recibir datos. También disponemos de dos métodos para encolar datos en un flujo de trabajo, es decir enviarle datos EnqueueItem y EnqueueItemOnIdle.

Una par de cosas más, GetWorkflowDefinition, nos devuelve la Actividad raíz de nuestro flujo de trabajo, esta actividad podemos castearla a SequentialWorkflowActivity, si la instancia es un flujo de trabajo secuencial, con lo cual obtendríamos una foto del flujo de trabajo. Cuando digo una foto es por que NO es el flujo de trabajo que se está ejecutando si no una imagen de las actividades que componen el flujo de trabajo. Esto puede ser interesante, para mostrar una representación de lo que contiene un flujo de trabajo o para realizar modificaciones dentro de nuestro flujo de trabajo. ApplyWorkflowChanges, se encargará de aplicar las modificaciones dentro de los flujos de trabajo.

Por último ReloadTrackingProfiles, volverá a cargar un perfil de rastreo o monitorización para una instancia determinada, lo veremos cuando veamos el servicio de seguimiento (Tracking).

2do Asalto – Las primeras actividades







Por el momento conocemos que es lo que hace DelayActivty, bien, animemos un poco esto con algunas nuevas actividades a la vez que vamos viendo otras cosas, veamos la actividad While, (WhileActivity) esta actividad nos permitirá controlar el flujo de nuestro flujo de trabajo (otra vez redun.). La actividad While es una actividad compuesta, dentro de esta actividad podemos ejecutar otra actividad. Si deseamos ejecutar más de una actividad dentro tendremos que usar una actividad llamada SequenceActivity, que es también  una actividad compuesta, en la que dentro podemos colocar una o más actividades.


La actividad WhileActivity, tiene una propiedad necesaria para su ejecución, esta es una condición que debemos establecer, condición que se evaluará desde que esta actividad comience y cada vez que se termine de ejecutar la actividad que se encuentra dentro, es similar al bucle while de cualquier lenguaje de programación, fijaros que en c#. c, pascal etc, podemos ejecutar un while sin poner {} ó begin, end; poniendo en su interior una sola sentencia, y que para ejecutar más de una tenemos que poner algo que indique que lo que viene a continuación es una secuencia, eso es lo que hace el SequenceActivity. Podemos establecer esta condición de dos maneras distintas, por código y usando una regla. Las reglas son también declarativas archivo .rules.


En la nueva y remodelada versión del programa (en la primera versión, pretendía que os centrarais en WF y no en el programa Winforms, a la vez que veíais como estaba estructurado el proyecto), tenemos un flujo de trabajo llamado WorkflowLongWhile, este flujo de trabajo tiene un bucle while y una actividad de código en su interior, CodeActivity.


La actividad CodeActivity lo que hace es ejecutar un trocito de código, el que nosotros queramos, que ha de establecerse en la propiedad ExecuteCode. En este caso lo que hacemos es incrementar el contador, en el ejemplo adjunto tenéis este flujo de trabajo en versión código y en versión declarativa. Abrid el archivo xoml y el rules para ver como son. De momento solo verlo, más adelante veremos como se ejecutan los archivos .xoml.


Notar como a diferencia de los flujos de trabajo que usan un timer (Delay y Delay2) LongWhile no se pone en estado idle, esto es debido a que en ningún momento se encuentra en inactividad, no espera a nada ni a nadie. Si le preguntamos por GetWorkflowNextTimerExpiration, veremos que devuelve 31/12/9999 23:59:59,  eso es que no espera recibir un evento del timer nunca, o por lo menos antes de esa fecha 🙂 .  


Bien, si tenéis dudas sobre lo comentado (ojo hay mucho todavía) podéis ponerlas en los comentarios. Antes de terminar con esta parte vamos a ver como podemos pasar datos a nuestros flujos de trabajo. Los flujos de trabajo nos sirven para modelar la lógica de nuestros procesos (si puedo colgaré el video del frito de gamba), y la mayoría de estos requieren de datos para poder hacer el trabajo, existen distintas formas de hacer llegar esta información a a nuestros flujos de trabajo, en esta parte veremos como pasar parámetros.


Para pasar un parámetro a un flujo de trabajo primero necesitamos definir una propiedad pública dentro de la clase que contiene nuestro flujo de trabajo, este propiedad será la receptora. Para hacerle llegar el parámetro, debemos  invocar el método del runtime CreateWorkflow, pasándole un diccionario que contendrá los parámetros que deseemos.  Runtime.CreateWorkflow(typeof(<nuestro flujo de trabajo>),<Diccionario de parámetros>) 


En el ejemplo adjunto tenemos un flujo de trabajo llamado SumNumbers que realizará una simple suma con dos números. Podéis rellenar estos en la pestaña de parámetros (Number A, Number B). Cuando este flujo de trabajo termine, presentará en el log el resultado. Este resultado debemos recogerlo en el evento del runtime Completed, así que mirar como ha cambiado.


Aplicación de ejemplo – 2


Si, un poco más bonita (ira mejorando mucho), el código esta organizado, espero que no tengáis problemas con mi spanglish (yo sufro de esto, codifico en ingles con comentarios en castellano, en fin nadie es perfecto) bueno cosas para ir jugando, podéis usar el ejemplo de sumar dos números con otras actividades, como por ejemplo IfElse. La idea del código era que los alumnos jugaran con el, así que cualquier sugerencia al respecto, experimento o BUG por favor comentarlo.

La lista con las instancias ahora monitoriza el estado en tiempo real (hablaremos de esto) y para realizar acciones sobre las instancias (flujos de trabajo), hay un menu contextual.


 


 Código del 2do ASALTO – WinHostWorkflow_2.zip (39,56 KB)

Curso de Workflow Foundation (1)

Tenía que impartir un curso de Workflow Foundation que finalmente no voy a dar, ya que estoy muy liado con otros temas. Ayer por la noche pensé que era una pena desaprovechar todo este trabajo, más aún porque estaba deseando montar un curso distinto, me explico:


 El curso iba a consistir de 10 clases de una hora y media de duración (usease 15 horas) que no es mucho. De modo que tenía pensado lo siguiente. Una introducción de 45 minutos explicando cosas y mostrando un ejemplo práctico, ejemplo que previamente habría diseñado para la ocasión. Los 45 minutos restantes iban a ser para (30 min) que la gente trabajara con el código del ejemplo y lo enredara (mientras yo les explicaba el código). Por último, entre todos (siguiendo unas directrices 15min) iríamos añadiéndole funcionalidad al código, para ir viendo cada cosa en profundidad en la siguiente clase.


Bueno, finalmente esto no ha podido ser así, pero no voy a dejar ese trabajo muerto de risa en alguno de mis discos duros … de modo que lo voy a ir poniendo en el blog a ver qué pasa. El curso no está terminado del todo así que en función de la aceptación que tenga lo terminaré. Por favor poner los comentarios en ideseg.com …


1er Asalto – Introducción a Windows Workflow Foundation

Material necesario:



Windows Workflow Foundation es un framework a través del cual vamos  a poder crear nuestros propios motores de flujos de trabajo.


Este framework, está compuesto  por los siguientes elementos:

–  Un conjunto básico de actividades (primitivas) con las cuales vamos a poder modelar nuestros flujos de trabajo.
–  Un motor de flujos de trabajo que será el encargado de ejecutar los flujos de trabajo.
–  Un conjunto de servicios con los que podemos añadir funcionalidad a nuestro motor de flujos de trabajo.







Comencemos por el motor de flujos de trabajo.

Este motor es una clase llamada WorkflowRuntime, de la cual solo podremos tener una instancia por aplication domain, (por aplicación). Esta aplicación será la encargada de albergar nuestro motor de flujos de trabajo, y puede ser cualquiera de las aplicaciones que desarrollamos con Visual Studio, una aplicación Winforms, una aplicación ASP:Net, un servicio web ó una aplicación de consola.

Esta clase, tiene tres constructores, el primero sin parámetros y los otros dos, nos permiten pasarle datos que podemos albergar en un archivo de configuración.

La instancia de esta clase será nuestro motor de flujos de trabajo. Esta clase, implementa los interfaces IDisposable y el IServiceProvider. El primero es debido a que internamente el motor se encargará de liberar los recursos usados (método Dispose), de modo que poco nos tiene que importar, solo saber que el los gestionará. El segundo, es que implementa un proveedor de servicios de manera que pasará ciertos objetos a otras clases (método GetService).
Estos servicios son los que he mencionado anteriormente, servicios a través de los cuales podemos añadir funcionalidad a nuestro motor de flujos de trabajo.

Para manejar estos servicios en el motor de flujos de trabajo, tenemos cuatro métodos, AddService – para añadir un servicio, GetAllServices – que nos devolverá una colección con los servicios que estamos usando, GetService – que nos devolverá un servicio especifico y RemoveService – mediante el cual podemos eliminar un servicio.

Para manejar flujos de trabajo dentro del motor, tenemos CreateWorkflow que creará un nueva instancia de un flujo de trabajo para ejecutarse dentro del motor. Tenemos dos maneras básicas de crear flujos de trabajo, leyéndolas desde un archivo xoml, o indicando el tipo. GetLoadedWorkflows – que nos devolverá una colección con los flujos de trabajo que se encuentran cargados en el motor y GetWorkflow que nos devolverá un flujo de trabajo usando el Id de la instancia.

Por  último (ya veis que se trata de una complicadísima clase) tenemos dos métodos uno para arrancar el motor StartRuntime, el cual no es necesario usar si arrancamos una instancia dentro (es una obviedad, pero al iniciarse, la propiedad IsStarted se pondra en true) y StopRuntime que detendrá el motor.

El resto de la clase son eventos que se dispararán en función de las cosas que vayan ocurriendo en el motor. Start y Stopped, si se arranca o detiene el motor de flujos de trabajo, y todos los demás a excepción de ServicesExceptionNotHandled que se disparará cuando ocurra una excepción no manejada en alguno de los servicios, nos dan información sobre lo que ocurre en las distintas instancias de flujos de trabajo que se están ejecutando en el motror.

Cada uno de estos, se corresponde con el estado en que puede encontrarse una instancia, flujo de trabajo dentro del motor. Aborted, Completed, Created, Idled, Loaded, Persisted, Resumed, Started, Suspended, Terminated y Unloaded.

Bien, por el momento parece que no esto no es complicado, solo necesitamos instanciar esta clase para tener un motor de flujos de trabajo. Pero esto sería como un coche sin pasajeros (flujos de trabajo). Podemos arrancarlo, pararlo pero no hace nada más.


Los flujos de trabajo están compuestos por actividades, WF, nos facilita el trabajo dándonos un conjunto básico de actividades que son las que podemos ver a la izquierda en Visual Studio (panel de herramientas). Una vez agrupadas estas actividades formarán parte de una clase que a su vez heredará de uno de los tipos posibles de flujos de trabajo.







Si estamos creando un flujo de trabajo secuencial, heredará de SequentialWorkflowActivity, aquí un inciso sobre como está mostrada la estructura de clases, un SequentialWorkflowActivity hereda de SequenceActivity que es un conjunto de actividades secuenciales, SequencyActivity hereda de CompositeActivity que representa todas las actividades compuestas y que a su vez hereda de Activity que es una actividad simple.


Si por el contrario estamos creando un flujo de trabajo de máquina de estados, se heredará de StateMachineWorkflowActivity, que representa un flujo de trabajo basado en estados y a su vez hereda de StateActivity que representa un estado. Podéis ver la estructura de clases en el diagrama de la derecha..


Por el momento basta con esto, lo que nos interesa en este momento es que un flujo de trabajo es un conjunto de actividades y en este caso vamos a realizar un flujo de trabajo secuencial, con lo que arrastraremos una actividad en nuestro flujo de trabajo.


Si observamos el diseñador de flujos de trabajo, lo que ocurre es similar a lo que pasa con ASP.Net o WinForms, lo que hace VS es mantener una clase parcial, compuesta por dos archivos, en el primero tendremos nuestro código limpio y en el segundo lo que el diseñador vaya generando.


Si miramos, lo que hay en el archivo del diseñador (vista de código) lo que hace es crear un atributo con cada actividad del tipo correspondiente a la actividad que hemos arrastrado. En el método Initialize, lo que hace es ir añadiendo la actividad a una colección de Activities. Esta colección viene de la clase CompositeActivity. Ya que una actividad compuesta, esta basada en una colección de actividades.


Bien, nuestro primer flujo de trabajo va ha ser de lo más sencillo, simplemente vamos a añadir a nuestro flujo de trabajo una actividad, una actividad DelayActivity que es un temporizador. Este detendrá la continuación del flujo de trabajo, en el ejemplo esta puesto en 45 segundos.



Como os he dicho es un curso muy sencillo así que por el momento solo hay que abrir el proyecto adjunto y estudiarlo.


Aplicación de ejemplo – 1


La Aplicación de ejemplo es una simple aplicación WinForms, que nos va a permitir lanzar flujos de trabajo dentro de un motor (Run Workflow), estos se irán ejecutando he iremos viendo su estado a través de los eventos recibidos en el motor de flujos de trabajo (Engine Status).



También disponemos de una vista con las instancias de los flujos de trabajo (Loaded Workflows) que se están ejecutando dentro del motor. A la izquierda podemos enviar acciones a esos flujos de trabajo (Suspend, Resume, Terminate, Abort, Unload) para ir viendo que ocurre. El botón Unload, está desactivado (lo usaremos más adelante, ya que necesitamos tener el servicio de persistencia activado para poder descargar los flujos de trabajo).


También he añadido un botón para ver en que estado se encuentra un flujo de trabajo, este nos informará del cuando le va ha llegar al flujo de trabajo un evento de tipo timer.


Código del ejemplo – 1 


El código esta compuesto por dos proyectos, uno la aplicación Winforms con el Motor de flujo de trabajo y el otro es una biblioteca de flujos de trabajo, con el flujo de trabajo que hemos visto antes.


– Lanzar flujos de trabajo dentro del motor y ver que pasa
– Usar las acciones sobre las instancias de los flujos de trabajo
– Arrancar y parar el motor (con flujos de trabajo en ejecución)
– Probar a crear otros flujos de trabajos (ej. dos o tres delays) e ir ejecutándolos dentro del motor


  Código del 1er ASALTO – WinHostWorkflow.zip (29,25 KB)