Junio de giras

Este Junio va a ser un mes movidito en cuanto a viajes, la verdad es que para mi siempre es un placer impartir charlas de desarrollo y sin duda este mes me lo pasaré en grande. En primer lugar con las charlas en el CodeCamp estos 2 y 3 de Junio hablando con Alejandro Mezcua, parecemos inseparables en estos eventos, y después en el Ask the Expert y en el Diván de Mónica ( que nos esperará J….).

Una vez terminado el CodeCamp empezarán los preparativos para la gira de ISV Innovation Days dónde pararé por Barcelona, Zaragoza, Madrid y Sevilla los días 18,19,20 y 21 presentando las novedades de ‘Orcas’ ( que no son pocas claro… ).

 

Espero ver muchas caras conocidas y seguir teniendo el placer de conversar con tanto ‘geek’ como yo…

 

 

Un saludo

Unai

ReMIX






El ReMIX es el principal evento de Microsoft para los diseñadores y desarrolladores Web más vanguardistas. Dos días de duración, 20 sesiones en 2 tracks paralelos y los mejores ponentes nacionales e internacionales nos permitirán entablar contacto con las tecnologías más innovadoras y las nuevas oportunidades de negocio de la Web. Durante estas jornadas Microsoft, de la mano de Forest Key, director de producto de la división de servidores y herramientas de Microsoft, presentará Silverlight (Codename WPF/E).


Transactional-aware services en WF

EL PROBLEMA:


Ya hace algún tiempo comenté algunos temas sobre el servicio de persistencia de Windows Workflow Foundation, incluso puse los materiales de una charla Open Day 2006 en las oficinas de Microsoft que trataban este tema, sin duda, es un tema de los más importantes sobre los que uno puede hablar de Workflow Foundation ya que afecta en gran medida a parámetros de escalabilidad y a parámetros de infraestructura, sin dejar de lado el concepto de ‘long running process’ que este servicio nos proporciona. Una de las demos principales sobre este servicio es siempre el enseñar cómo una vez persistida una instancia de WF, bien sea por la decoración de una actividad con el atributo PersistOnClose, porque se ha pasado por una actividad que persiste de forma automática como TransactionScope o bien porque el parámetro de configuración UnLoadOnIdle del servicio de persistencia está a true , si rompemos el proceso de host que aloja el runtime de Workflow Foundation podemos volver a reiniciar a partir del último estado la instancia de Workflow en el momento en el que otro runtime se inicia, esto tiene algunos pequeños toques sobre los que seguramente podría hablar, pero lo dejamos para otro post J. Esto sin duda es de gran utilidad ya que aseguramos que aunque algún host de WF se caiga podemos continuar con la ejecución de la instancia de un WF a partir del último estado en el que queda, pero tiene una ligera problemática con la que hay que tener especialmente cuidado. Imaginaros el siguiente caso, estamos ejecutando la instancia de WF que se puede ver en la siguiente imagen, dónde hay varios puntos de persistencia, uno después de ejecutar el paso 1 y otro después de ejecutar el paso 3, ¿qué pasará si el host se rompe después de ejecutar el paso 2 y antes del paso 3? La respuesta es que tenemos el estado guardado después de paso 1 por lo que cuando otro runtime pueda ejecutar esta instancia persistida volverá a ejecutar el paso 2 y si se vuelve a romper se volverá a ejecutar cuando otro runtime vuelva a levantar el estado del store de persistencia. En muchas ocasiones esto no es del todo problemático puesto que pueden ser actividades que no modifiquen ningún tipo de elemento, actividades de consulta o de parametrización de la instancia, pero otras veces el que una actividad se ejecute varias veces puede no ser admisible, imaginaros que esa actividad que se ejecuta varias veces es un ingreso en cuenta bancaria o cualquier otra modificación en una base de datos, ¿qué hacemos entonces con esta problemática? ( Por supuesto no me vale andar poniendo infinitos puntos de persistencia similares al que se puede ver en la imagen anterior).




LA SOLUCIÓN:


La solución a la problemática expuesta anteriormente está en el uso de un par de interfaces que WF pone a nuestra disposición, estas interfaces son IWorkBatch y IPendingWork, la primera de ellas nos provee la posibilidad de añadir elementos de trabajo ( implementación es de IPendingWork ) a un WorkBatch. Un WorkBatch, por decirlo de forma sencilla, es un conjunto de elementos de trabajo que tienen garantizada una ‘ejecución única’ puesto que esta se retarda hasta un punto de persistencia o bien hasta que la instancia de worfklow se compromete o termina. La implementación de nuestra actividad dentro de un WorkBatch permite la solución a la problemática anterior aunque tiene un pequeño ‘efecto colateral’ que consiste precisamente en ese retardo de la ejecución de la actividad lo que puede hacernos parecer que ejecuciones de actividades posteriores a una que este dentro de un WorkBatch se ejecuta antes que la citada.


LA RESOLUCIÓN:


La resolución consiste en la realización de un par de pasos, el primero es hacer que la ejecución de nuestra actividad se base en un ‘servicio’, esto sin duda ya es de por sí una buena práctica en cuanto a diseño pero además nos va a permitir que nuestro servicio implemente IPendingWork para que pueda participar en un WorkBatch. A continuación se muestra una posible implementación de una actividad que hace una escritura de un mensaje basándolo en un servicio que implementa IPendinWork


using System;


using System.Collections.Generic;


using System.Text;


using System.Workflow.ComponentModel;


using System.Workflow.Runtime;


namespace WorkflowConsoleApplication1


{


    public abstract class PromptService


    {


        public abstract void WritePrompt(string prompt);


    }


    public class ConsolePromptService


        :PromptService,IPendingWork


    {


        public override void WritePrompt(string prompt)


        {


            IWorkBatch workBatch = WorkflowEnvironment.WorkBatch;


            workBatch.Add(this, prompt);


        }


        #region IPendingWork Members


        public void Commit(System.Transactions.Transaction transaction, System.Collections.ICollection items)


        {


            foreach (string s in items)


                Console.WriteLine(s);


        }


        public void Complete(bool succeeded, System.Collections.ICollection items)


        {


            //Código a ejecutar despues del commit


        }


        public bool MustCommit(System.Collections.ICollection items)


        {


            return true;


        }


        #endregion


    }


    public class PromptActivity


        :Activity


    {


        public static DependencyProperty PromptProperty = DependencyProperty.Register(«Prompt», typeof(String), typeof(PromptActivity));


        public string Prompt


        {


            get


            {


                return (string)base.GetValue(PromptProperty);


            }


            set


            {


                base.SetValue(PromptProperty, value);


            }


        }


        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)


        {


            PromptService promptService = executionContext.GetService<PromptService>();


            if (promptService != null)


                promptService.WritePrompt(this.Prompt);


                


            return ActivityExecutionStatus.Closed;


        }


    }


}


Ahora que ya tenemos nuestra actividad creada con el uso de un servicio que implementa IPendingWork agregamos el servicio al Runtime de WorkflowFoundation


static void Main(string[] args)


{


using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())


{


                SqlWorkflowPersistenceService persistenceService = new SqlWorkflowPersistenceService(«Server=.;Initial Catalog=WFPersistenceService;Integrated Security=true», true, new TimeSpan(0, 0, 10), new TimeSpan(0, 0, 15));


                workflowRuntime.AddService(persistenceService);


                ConsolePromptService consolePromptService = new ConsolePromptService();


                workflowRuntime.AddService(consolePromptService);


AutoResetEvent waitHandle = new AutoResetEvent(false);


workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) {waitHandle.Set();};


workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)


{


Console.WriteLine(e.Exception.Message);


waitHandle.Set();


};


WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));


instance.Start();


waitHandle.WaitOne();


}


}


Y modificamos nuestro Workflow sustituyendo los CodeActivity por nuestra actividad PromptActivity y agregamos una actividad de código entre dos de las nuestras con un sleep para que nos de tiempo a romper intencionadamente el Host.



LAS CONCLUSIONES:


En este post acabamos de ver como crear actividades cuya ejecución se realice dentro de un WorkBatch y conseguir que la ejecución de las mismas esté garantizada a una única vez aunque el ‘host’ se rompa. Os adjunto una demo, Transactional-aware services en WF , que consiste en una solución con el proyecto de workflow y las actividades creadas así como otro proyecto que únicamente arranca un host para compromar todo lo que hemos estado comentando…


Espero que lo disfrutéis


Unai Zorrilla Castro