Miguel Angel Morán

Machines take me by surprise with great frecuency...

May 2007 - Artículos

Workflow Foundation: El Banco del Workflow

El "Banco del Workflow" es un proyecto que hice para la gira de Pronet CodeCamp y que si fueron a cualquiera de estos eventos, lo debieron de haber visto ya sea presentado por mi o por otros speakers. El proyecto se trata de un banquito que funciona con puras reglas de negocio basadas en flujos de trabajo. Contiene 7 demos que implementan bastante funcionalidad de Workflow Foundation, entre ella:

  • Hostear el runtime de Workflow
  • Invocación de webservices
  • Utilización de actividades paralelas
  • Creación de actividades personalizadas (además de esto será mi siguiente post, pero por si no pueden esperar pueden bajar el demo de una vez jeje)
  • Utilización de runtime services (se muestra tracking services)
  • Exponiendo workflows como webservices
  • Utilizando XOML para  definir webservices

En la gira de Pronet lo máximo a lo que llegué es al demo 3 porque se me acababa el tiempo juar juar, así que aunque hayan asistido a la gira encontrarán harto contenido inédito.

El zip que ustedes pueden bajar aquí, trae las instrucciones de instalación, la base de datos que utiliza para la operación de los webservices y para utilizar el servicio de tracking services, y los webservices utilizados como tal.

El proyecto tiene dos carpetas

  • Solution: Tiene el contenido listo para usarse previa instalación del proyecto
  • Starter: Tiene los "templates" para que ustedes puedan seguir las instrucciones que encontrarán aquí  que es un documento donde viene paso a paso como hacer el demo y llegar al objetivo.

Espero que lo encuentren interesante, creo que es un buen comienzo para que se introduzcan en el estudio de Workflow Foundation.

A continuación unos pantallazos del proyecto:

 

 

Saludos a todos y

 

¡Feliz Codificación!


Miguel A. Morán B.
Microsoft MVP Visual Developer

Cargando interfaces gráficas y eventos dinámicamente con XAML

Introducción

Una de las cosas que más me agradan de los lenguajes basados en XML es la capacidad de poder ser cargados en tiempo de ejecución si necesidad de recompilar el código completo.

Esto trae muchos beneficios sobre todo en las empresas que requieren una operación de altísima disponibilidad y que no pueden parar ni un segundo su operación o en aquellas organizaciones cuyos datos son muy cambiantes y que requieren una gran agilidad para poder operar adecuadamente y de acuerdo al ritmo que su propio negocio les va dictando.

Tanto Windows Presentation Foundation, como Windows Workflow Foundation, como Windows Communication Foundation soportan en diferentes formas y a diferentes niveles el cargado dinámico de markup para ofrecernos los beneficios anteriormente descritos.

En el caso de WF es posible describir completamente en XOML un flujo de trabajo y pasar del motor de ejecución (Workflow Runtime) de flujos de trabajo la ruta de un archivo de markup, en el caso de WCF toda la configuración de los “fierros” (como canales de comunicación, contratos, etc) se hace a través de configuración en archivos basados en XML.

En el caso de WPF, que es el tema que trataremos en este artículo, es posible cargar dinámicamente un archivo XAML y generar un objeto a partir de él para añadirlo la jerarquía de objetos que se encuentren cargados dentro de la ventana o página de WPF.

Ahora bien, como en todo, no todo es miel sobre hojuelas. El hecho de que un motor de ejecución tenga que cargar dinámicamente lenguaje makup implica necesariamente poder de procesamiento lo cual de una u otra manera puede impactar en el performance general de nuestra aplicación.

Otro de los problemas a los cuales nos enfrentamos al momento de querer cargar dinámicamente un archivo de markup es que la implementación de la clase XAMLReader del .NET Framework qué es la encargada de deserializar los objetos no lee ni serializa los eventos y el código agregados a un objeto en específico es decir si nosotros definiremos por ejemplo un botón y este botón tuviera código para manejar el evento click o cualquier otro evento, al momento de ser serializado con la clase XAMLWriter perdería esta información, todo esto sucede derivado a que el XAML debe ser compilado antes de poder ser ejecutado.

 En este capítulo presentaremos una manera en la cual es posible cargar dinámicamente archivos de markup y no sólo eso, sino también presentamos una manera alternativa para persistir información de eventos y que ésta no se pierda al momento de la Serialización y que sea posible asigna dinámicamente los eventos al momento cómo hacer que el markup que se cargue tenga las instrucciones para ejecutar un código del lado del host.

 Manos a la obra

1) Para empezar definiremos una ventana transparente, ustedes, desde luego pueden quitarle o ponerle payasaditas de presentación. Lo importante de nuestro código está en jumboletrotas: Un grid llamado GridContenedor que adentro tiene un textblock y un botonzote grandote y rojo que cuando lo apretemos cargará dinámicamente un archivo XAML. El código es el siguiente

<Window AllowsTransparency="True"  WindowStyle="None"  x:Class="Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="DevelopersDotNet" Height="484.48695652174" Width="529.565217391304"  Opacity=".7">

 

    <Grid Name="GridContenedor">

      <TextBlock HorizontalAlignment="Center">Haz click en el boton y veras una linda sorpresa

      <LineBreak />

      <Bold>XAML Cargado dinamicamente!!!!</Bold>

      </TextBlock>

      <Button  BorderThickness="0" Name="Botoncito" Margin="80.1391304347826,96,87.6521739130435,65.9478260869563"  BorderBrush="Transparent">

 

        <Canvas Width="350" Height="300" >

          <Rectangle Width="350" Height="300" RadiusX="10" RadiusY="10"

          Stroke="Green" StrokeThickness="10" Fill="Red">

          </Rectangle>

          <TextBlock Margin="20,100,10,10" FontSize="60" Text="DevDotNet!"

    Foreground="Cyan">

        

            <TextBlock.BitmapEffect>

              <DropShadowBitmapEffect

                 Noise ="1"

                ShadowDepth="8"

                Direction="200"

                Color="Blue"

                Opacity="0.75"

                Softness="0.0" />

            </TextBlock.BitmapEffect>

           

          </TextBlock>

         </Canvas>

 

      </Button>

    </Grid>

  </Window>

 

Al momento de ejecutar la aplicación deberíamos tener un resultado como el siguiente:

Se ve rebonito ¿no? Como es transparente atrás pueden ver mis gadgets y todo el asunto. Bueno, pero no estamos aquí por lo bonito sino por lo funcional.

2) Posteriormente vamos a crear el achivo XAML que cargaremos dinámicamente, pueden ustedes añadirlo a su proyecto o bien escribirlo en el Notepad

Recuerden que este archivo es el que cargaremos dinámicamente por lo que NO es necesario que lo agreguen a la solución, pueden ponerlo en cualquier carpeta de su PC y como dije anteriormente si quieren sólo darle copy a este código y paste en el notepad :D

En este archivo definiremos lo siguiente

<StackPanel Margin="35.704347826087,16.5478260869566,35.1652173913044,24.3130434782609" MinHeight="50" MinWidth="50" Name="StackPanel1"

            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <StackPanel.Background>

          <RadialGradientBrush xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">

            <GradientStop  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Color="GreenYellow" Offset=".3"></GradientStop>

            <GradientStop xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Color="Red" Offset=".7"></GradientStop>

            <GradientStop xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Color="Blue" Offset=".2"></GradientStop>

          </RadialGradientBrush>

        </StackPanel.Background>

 

  <Label xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Height="28.8039130434783" Margin="90.1172463768117,27.7265217391304,86.0869565217392,0" Name="Label1" VerticalAlignment="Center" FontSize="16" >Soy un objeto del tipo label!</Label>

  <TextBox xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Opacity=".5" Margin="56.6608695652174,114.008695652174,66.8869565217391,114.469565217391" Name="TextBox1"  VerticalAlignment ="Top" ></TextBox>

  <Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  Tag="Click,Saludar1" Height="23" Margin="100.382608695652,0,103.617391304348,36.8347826086957" Name="Button1" VerticalAlignment="Bottom" >Haga click en mi</Button>

  <Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  Tag="MouseLeave,Saludar2" Height="23" Margin="100.382608695652,0,103.617391304348,36.8347826086957" Name="Button2" VerticalAlignment="Bottom" >Pase el mouse sobre mi</Button>

 

 

</StackPanel>

Como podemos ver estamos definiendo un objeto contenedor llamado StackPanel1 que adentro tiene varios controles (un label , un textbox y dos botnones) y que define un fondito de gradiente circular. Ignoraremos por un momento las letras monstruo, posteriormente veremos cual es su objetivo… Yo nombré a este archivo Serializado.xaml

Ahora viene lo bueno vamos a tirar código para cargar dinámicamente el archivo XAML, iremos al codebehind de nuestro archivo Window1.xaml (es decir, NO el que acabamos de hacer, sino el anterior al que acabamos de hacer, es decir NO el de los stackpanels, es decir el que tiene el GridContenedor, quedó claro? ;)) y añadiremos el siguiente código

 

    Public Sub LeerXAML(ByVal s As Object, ByVal e As System.Windows.RoutedEventArgs) _

    Handles Botoncito.Click

 

        Try

            'Escondo  el boton para ver el XAML cargado

            Me.Botoncito.Visibility = Windows.Visibility.Hidden

 

            'Abro un streamreader para cargar el XAML que esta en un archivo, cambiar

            'la ruta definida aquí por la ruta donde se encuentre el archivo a cargar dinamico

 

            Dim lobjLectorStream As StreamReader = New StreamReader("C:\Users\Bichi\Documents\Visual Studio 2005\Projects\XAMLDinamico\XAMLDinamico\XAMLDinamico\Serializado.xaml")

 

            'Declaro e instancio un stackpanel

            Dim lobjPanel As New StackPanel

            Dim lobjLectorXAML As Object = XamlReader.Load(lobjLectorStream.BaseStream)

 

            'Añado el stackpanel (y sus objetos children) al gridcontenedor del window

            Me.GridContenedor.Children.Add(CType(lobjLectorXAML, StackPanel))

        Catch ex As Exception

            MsgBox("Ocurrio el siguiente error " & ex.ToString())

        End Try

 

 

    End Sub

 

 

Al momento de correr este código deberíamos ver lo siguiente:

 

Como podemos ver hemos hecho uso del método Load de la clase XamlReader; lo cual nos ha permitido cargar un archivo externo a nuestra aplicación y añadirlo en tiempo de ejecución a la ventana Window1. Sorprendente ¿no?

Sin embargo aunque todo parezca muy bonito, como dijimos anteriormente existe un problema con esta manera de deserializar un archivo en xaml y cargarlo dinámicamente, y este problema se refiere a que no es posible guardar información de eventos ni código ni siquiera con la etiqueta <x:Code> entonces nosotros llevaremos un poco más allá el ejemplo y definiremos una manera relativamente sencilla para guardar ese información de los eventos y mapearla hacia código administrado. Evidentemente si actualmente hacemos clic sobre cualquiera de los botones no hacen nada. Vamos a utilizar el mismo markup para obtener la información a los eventos a los que nos queremos suscribir. Es aquí donde haremos uso del código escrito en letrotas de nuestro archivo serializado.xaml

Regresando a dicho archivo podemos encontrar nosotros en la definición del botón que hemos hecho uso de la propiedad Tag; como todos sabemos esta propiedad súper añeja sirve para guardar información acerca de un control que en pocas palabras no cabe ningún otro lado.

En el primer button hemos definido Tag="Click,Saludar1", mientras que en el segundo Tag="MouseLeave,Saludar2" no hace falta pensar mucho para suponer que en el primer botón suscribiremos al evento click en el método saludar1, y el segundo botón será suscrito en el evento MouseLeave al método Saludar2.

Es importante señalar que las funciones y métodos que serán invocados al momento de realizar el mecanismo de publicación suscripción deberán en el ensamblado de aplicación que cargará dinámicamente el archivo XAML, aunque también es posible cargarlos en otra dll haciendo ajustes mínimos.

3) Vamos a modificar ahora la función LeerXAML a la cual agregaremos un ciclo for each que se encargará de barrer todos los objetos cargados dinámicamente y en el caso de que su tipo sea button utilizaremos reflection para ser la asignación del evento al método definido en el markup. Quiero aclarar que aun en el escenario que utilizaramos C# para este ejemplo de todos modos debemos de usar reflexión toda vez que no sabemos a que evento se quiere suscribir el objeto debido a que esa definición se encuentra en  el XAML, por lo que no podrán utilizar el operador += de C# o la sentencia AddHandler de Visual Basic para suscribir el evento debido a que no lo sabemos que evento vendrá definido en el archivo XAML.

Pero como bien dicen un código es mejor que 1000 palabras, así que aquí les va el poder:

 

    Public Sub LeerXAML(ByVal s As Object, ByVal e As System.Windows.RoutedEventArgs) _

   Handles Botoncito.Click

 

        Try

            'Escondo  el boton para ver el XAML cargado

            Me.Botoncito.Visibility = Windows.Visibility.Hidden

 

            'Abro un streamreader para cargar el XAML que esta en un archivo

            Dim lobjLectorStream As StreamReader = New StreamReader("C:\Users\Bichi\Documents\Visual Studio 2005\Projects\XAMLDinamico\XAMLDinamico\XAMLDinamico\Serializado.xaml")

 

            'Declaro e instancio un stackpanel

            Dim lobjPanel As New StackPanel

            Dim lobjLectorXAML As Object = XamlReader.Load(lobjLectorStream.BaseStream)

 

            'Hago un barrido de todos los objetos Button para asociarlos al manejador

            'dinamico de eventos

 

            Dim lobjBoton As Button

            Dim lstrInfoSuscripcion() As String

 

            For Each lobjControl As Control In CType(lobjLectorXAML, StackPanel).Children

 

                If lobjControl.GetType() Is GetType(Button) Then

                    lobjBoton = CType(lobjControl, Button)

                    lstrInfoSuscripcion = lobjBoton.Tag.ToString.Split(",")

 

                    'Utilizo reflection para hacer la suscripción dinámica al evento

 

                    Select Case lstrInfoSuscripcion(0)

                        Case "Click"

                            AgregarManejadorEvento(lobjBoton, lstrInfoSuscripcion(0), _

                            New RoutedEventHandler(AddressOf InvocarMetodo))

 

                        Case "MouseLeave"

                            AgregarManejadorEvento(lobjBoton, lstrInfoSuscripcion(0), _

                            New Input.MouseEventHandler(AddressOf InvocarMetodo))

 

                    End Select

 

MsgBox("Se realizó exitosamente la suscripción del evento " & lstrInfoSuscripcion(0) & _

                       " al método " & lstrInfoSuscripcion(1) & _

                       " en el objeto " & lobjControl.Name)

 

                End If

            Next

 

            'Añado el stackpanel (y sus objetos children) al gridcontenedor del window

            Me.GridContenedor.Children.Add(CType(lobjLectorXAML, StackPanel))

        Catch ex As Exception

            MsgBox("Ocurrio el siguiente error " & ex.ToString())

        End Try

 

 

    End Sub

Como podemos ver en el código anterior para cada uno de los eventos que tengamos contemplados manejar debemos hacer la suscripción personalizada debido a que cada manejador de evento recibe parámetros diferentes de acuerdo al tipo de delegado definido, por lo cual elegí hacer una sobrecarga de un método al que llamé InvocarMetodo. La suscripción como tal, que tiene el código de reflexión se hace en el método AgregarManejadorEvento, que presento a continuación

 

Public Function AgregarManejadorEvento(ByVal lobjControl As Object, _

        ByVal lstrNombreEvento As String, _

        ByVal ldelManejadorEvento As [Delegate]) As _

        Boolean

 

        Dim ltypTipo As Type = lobjControl.GetType()

 

        ' Obtiene la informacion del evento si no la encuentra se sale

        Dim lobjInfoEvento As EventInfo = ltypTipo.GetEvent(lstrNombreEvento)

        If lobjInfoEvento Is Nothing Then Return False

        Try

            lobjInfoEvento.AddEventHandler(lobjControl, ldelManejadorEvento)

            Return True

        Catch ex As Exception

            MsgBox("Ocurrió un error al suscribir el evento " & ex.ToString())

            Return False

        End Try

    End Function

Y estos son los métodos sobrecargados que invocan a los métodos definidos en el markup previa suscripción al evento

Public Sub InvocarMetodo(ByVal o As Object, ByVal e As RoutedEventArgs)

        Dim lstrMetodoAInvocar() As String = o.tag.ToString.Split(",")

        Try

 

            MsgBox("Se intentará invocar el método " & lstrMetodoAInvocar(1))

 

            Dim lobjMetodoAInvocar As Object = GetType(Window1).InvokeMember(lstrMetodoAInvocar(1), _

            BindingFlags.Default Or _

            BindingFlags.InvokeMethod, Nothing, Nothing, Nothing)

 

        Catch ex As System.MissingMethodException

            MsgBox("Imposible encontrar el método " & lstrMetodoAInvocar(1))

 

        End Try

 

    End Sub

 

 

    Public Sub InvocarMetodo(ByVal o As Object, ByVal e As Input.MouseEventArgs)

        Dim lstrMetodoAInvocar() As String = o.tag.ToString.Split(",")

        Try

 

            MsgBox("Se intentará invocar el método " & lstrMetodoAInvocar(1))

 

            Dim lobjMetodoAInvocar As Object = GetType(Window1).InvokeMember(lstrMetodoAInvocar(1), _

            BindingFlags.Default Or _

            BindingFlags.InvokeMethod, Nothing, Nothing, Nothing)

 

        Catch ex As System.MissingMethodException

            MsgBox("Imposible encontrar el método " & lstrMetodoAInvocar(1))

 

        End Try

 

    End Sub

 

4) Por último vamos a definir los métodos concretos que serán invocados después de la suscripción, es decir Saludar1 y Saludar2 definidos en el markup.

 

Public Shared Sub Saludar1()

 

        MsgBox("Invocación exitosa" & Chr(13) & _

    "Saludos a todos desde el metodo click descrito en el XAML!")

    End Sub

 

    Public Shared Sub Saludar2()

 

        MsgBox("Invocación exitosa" & Chr(13) & _

    "Saludos a todos desde el metodo mouseover descrito en el XAML!")

    End Sub

 

Al momento de ejecutar ahora nuestra aplicación, en el momento de la carga deberíamos ver los siguientes mensajes mostrados desde el método leer XAML

 

Ahora al hacer click sobre el primer botón deberíamos ver algo como esto:

Y posteriormente

Conclusión:

En este artículo hemos demostrado la manera de cargar dinámicamente un archivo de XAML y renderearlo Just In Time (dinámicamente), además aprendimos un workaround para, de alguna manera, persistir eventos en un archivo de markup utilizando el poder de Reflection.

Como siempre los invito a que examinen en código y mejoren la implementación original, me alegrará saber que les ha sido útil. Además a partir de este artículo y como lo prometimos anteriormente dejamos el código fuente listo para descargar.

Cualquier duda sobre este u otros artículos, siempre ando por los foros.

Saludos y de parte de todos

¡Feliz Codificación!
Miguel Ángel Morán B.
Microsoft MVP Visual Developer

 

Posted: 05-22-2007 1:44 AM por Bichi | con no comments |
.NET 3.0 Series Post 2. "Introducción a Workflow Foundation"

Jóvenes ilustres ¿Como están? Como siempre es un placer estar otra vez escribiendo sobre tecnología en Developersdotnet.

Estamos en una época y un momento del tiempo verdaderamente interesante ya que por parte de Microsoft estamos recibiendo casi diario nuevas entregas de tecnología nueva, lo cual es súper excitante pero también se hace muy difícil mantener un lugar donde podamos abarcar todo el uso de estas nuevas tecnologías y saber cómo resuelven nuestra vida como desarrolladores, sin embargo estamos haciendo todo lo que está en nuestro alcance y mi objetivo es hacer que ustedes tengan un lugar donde puedan encontrar  cómo usar la nueva tecnología, porque estamos bombardeados de publicidad y de sitios donde se nos dice que existen cosas nuevas pero son muy pocos lugares donde nos dicen exactamente como las debemos de usar y que problemas resuelven. Ese es el propósito de Developersdotnet, convertirse en un habilitador de uso para la tecnología Microsoft, todos los que hacemos este sitio esperamos realmente cumplir con nuestro objetivo.

Introducción

En ese artículo vamos a continuar con lo que empezamos hace ya algún tiempo y que por la emoción de hablar de ORM y de LINQ hemos dejado un poquito en el olvido. Vamos a continuar con la serie sobre. Net 3.0. En este caso hablaremos sobre qué es Windows Workflow Foundation y cuál es su razón de ser.

Uno de los objetivos de la programación orientada a objetos fue simplificar la representación estructuras programáticas usando como modelo objetos de mundo real.  El objetivo fue abstraer la realidad que nos rodea y representar con estructuras como clases, propiedades, métodos y eventos esos objetos con los que interactuamos todos los días y a toda hora y de esta manera simplificar la labor tanto del analista sistemas como las del desarrollador de software.

Las clases, los objetos, las propiedades y todas las estructuras que usamos en la programación orientada objetos nos proporcionan una manera más natural de interactuar con la computadora porque estamos utilizando conceptos antropomorfizados, esto es, objetos con forma y carterísticas humanas.

Los lenguajes de modelado como el UML o el BMPN desde su trinchera también han contribuido en este aspecto debido a que abstraen la realidad en forma de modelos que pueden ser entendidos no sólo por el equipo de desarrollo o los analistas sino también por los stakeholders (usuarios) de un proyecto de sistemas.

Sin lugar a dudas la antropomorfización de los sistemas nos ha traído bastantes ventajas en cuanto a productividad por lo cual las grandes empresas de computación (Microsoft, Sun, IBM, Oracle) siguen haciendo herramientas y generando tecnología sobre ésta línea de antropomorfización.

Procesos y flujos de trabajo.

               Todas las empresas y organizaciones humanas utilizan procesos para llevar a cabo sus objetivos cada una de las tareas que nosotros realizamos dentro de nuestra vida diaria tienen diferentes actividades que debemos terminar satisfactoriamente para que todo el proceso se cumpla y nosotros estemos en condiciones de decir que hemos terminado ese proceso que hemos llevado a cabo una tarea específica.

Los sistemas de calidad como ISO 9000  o los procesos de desarrollo como RUP o MSF son un conjunto de actividades ordenadas que en conjunto nos llevan a cumplir un objetivo determinado. Cuando nosotros nos ponemos a revisar todo esto nos damos cuenta que los procesos son parte de nuestra vida  diaria aunque en no siempre lo tengamos en cuenta.

Los procesos y los flujos de trabajo están íntimamente relacionados aunque no son exactamente lo mismo: Los procesos  generalmente son un súper conjunto de flujos de trabajo mientras que los flujos de trabajo son un conjunto de actividades relacionadas entre sí para llevar a cabo alguna tarea.

 El conjunto de los flujos de trabajo nos dará un proceso.

Hablando un poco más con orientación a sistemas de información los flujos de trabajo son un conjunto de pasos y actividades realizados por personas donde intervienen sistemas de cómputo para llevar ése control ordenado que al final nos llevará a un resultado concreto, por ejemplo la contratación de un empleado en una empresa requiere que se haga una o varias entrevistas, que se entregue y se registe su documentación

Windows Workflow foundation es la respuesta de Microsoft para representar estos flujos de trabajo y darnos una infraestructura completa y un tiempo de ejecución que nos permitirá describir flujos de trabajo de una manera más sencilla que utilizar exclusivamente un lenguaje de programación imperativo como Visual Basic o C#, además el motor de ejecución de workflows ofrece funcionalidad adicional que enriquece mucho el proceso de desarrollo de sistemas de flujo de trabajo como por ejemplo manejo de errores, tracking, persistencia, etc.

Al utilizar otras tecnologías presentes dentro del .NET Framework 3.0, (WPF, WCF) estamos en posibilidades de crear toda una plataforma informática que nos permita realizar aplicaciones orientadas a servicios, por ejemplo y contextualizando: Windows Presentation Foundation se encargará de generar los clientes con las interfaces gráficas llamativas y ricas, Windows Workflow Foundation se encargará de orquestar y de ordenar las actividades de la regla de negocio de nuestros sistemas mientras que Windows Communication Foundation se encargará de comunicar nuestra regla de negocios de diferentes instancias en forma quizás de un webservice, de MSMQ, etc.

 

Entrando en materia

Windows workflow foundation es una tecnología incluida dentro de. Net Framework que habilita a los programadores describir aplicaciones de flujo de trabajo de una manera sencilla y con una cantidad de servicios adicionales que aumentan significativamente su productividad. Para poder utilizar workflow fundation es necesario contar con el Net  framework 3.0 y para poder programarlo es necesario contar con visual studio 2005 y las extensiones de orcas para visual studio.

Windows workflow foundation es un marco de trabajo (o sea no es un servidor, ni un programa) que está compuesto de clases administradas para generar flujos de trabajo, y sus componentes principales son los siguientes:

Diseñadores: los diseñadores son las herramientas gráficas que se añaden a visual studio para modelar visualmente los flujos de trabajo

Librería de actividades base: es un conjunto de objetos expuestos a través del. Net Framework 3.0 que representa las actividades más importantes y más comúnmente utilizadas dentro de un flujo de trabajo entre ellas podemos encontrar actividades como el if, el while, la invocación de webservices, estas actividades se encuentran el toolbox de visual studio 2005 de la misma manera que sucede con Windows forms cuyo toolbox tiene botones, etiquetas, cajas de selección, etc.

Motor de ejecución de flujos de trabajo: podemos decir que es el corazón de Windows workflow fundation ya que se encarga de administrar y ejecutar los flujos de trabajo programados con WF

Servicios de ejecución: los servicios de ejecución como su nombre lo indica dan servicios para aumentar la funcionalidad de un flujo de trabajo entre ellos encontramos servicios de persistencia servicios de tracking entre muchos otros que nos permiten extender la funcionalidad de un flujo de trabajo

Para poder ejecutar un workflow es necesario albergarlo (sin albur) en un proceso host, como puede ser un servicio de Windows una aplicación de Windows forms, una aplicación de Windows presentation foundation, una aplicación ASP.Net etc. Porque como dijimos anteriormente workflow fundation no es una aplicación o un programa que se ejecuta independientemente, es necesario que nosotros programemos el host que a su vez alberg… :$ oh Dios, es decir, hospedará… al Runtime de Workflows.

 

1) El secuencial: Consiste en un flujo de trabajo que es muy semejante a un diagrama de actividades dentro del UML. Es decir hay un solo punto de inicio y un punto de terminación , que pasa por diferentes pasos (o actividades) para cumplir un propósito

2) El state machine: Representa a una máquina de estados dentro del UML, donde a diferencia del secuencial, tenemos pasos no deterministas y los pasos van cambiando de acuerdo a sus estados.

Manos a la obra

No sé por qué me pasa esto pero yo disfruto realmente una publicación que tiene como primer ejercicio un hola mundo creo que es bastante ilustrativo así que escribiremos nuestro primer flujo de trabajo en forma de un hola mundo. En la siguientes entregas estaremos profundizando un poco más sobre este asunto y prometo que la siguiente entrega será exclusivamente de workflow foundation. Una vez que se han instalado las extensiones de Workflow .

Haremos un flujo de trabajo secuencial

1)  abrir visual studio 2005

2) seleccionar Nuevo proyecto y seleccionar sequential workflow console application. Aqui lo que estamos haciendo es utilizar el template que automáticamente creará una aplicación de consola que tiene hospeda el runtime de Workflow Foundation e invoca a WF

 

3)  seleccionar la vista de diseño del workflow y arrastrar una actividad del tipo code hacia el diseñador de workflows y cambiar el nombre a holaworkflowactividad

4) dar doble click en la actividad recién creada y escribir el siguiente código

 

    Private Sub HolaWorkflowActividad_ExecuteCode(ByVal sender As System.Object, ByVal e As System.EventArgs)

        Console.WriteLine("Hola WF!!!")

    End Sub

 

5)  En el solution explorer seleccionar el archivo Module1.vb y localizar la función Main, y dentro de ella localizar la linea

 WaitHandle.WaitOne()

 inmediatamente después de esta instrucción colocar la siguiente linea de codigo

                Console.ReadLine()

6)  Ejecutar  la aplicación y verificar que se imprima el hola mundo

7) Antes de continuar analizaremos el código que generó el template, es decir el host (Module1.vb) para comprenderlo un poco más a detalle

Shared Sub Main()

'Se declara el runtime (el motor de ejecucion de workflows)

Using workflowRuntime As New WorkflowRuntime()

'Se añaden manejadores de eventos que se lanzarán al terminar el workflow

AddHandler workflowRuntime.WorkflowCompleted, AddressOf OnWorkflowCompleted

AddHandler workflowRuntime.WorkflowTerminated, AddressOf OnWorkflowTerminated

'Se declara una instancia de workflow

Dim workflowInstance As WorkflowInstance

'Creamos el workflow pasandole como parametro al runtime el tipo de nuestro workflow

'recordemos que los workflows como todo en .NET son objetos

workflowInstance = workflowRuntime.CreateWorkflow(GetType(Workflow1))

'Iniciamos el workflow

workflowInstance.Start()

WaitHandle.WaitOne()

End Using

End Sub

Un código similar (casi idéntico) es el que debemos de utilizar si queremos hospedar nuestro workflow en otra plataforma como Windows Forms, como WPF o ASP.NET

8)  Ahora haremos un pequeño ciclo con una condicion. Detener la aplicación e ir a la vista de código y declarar una variable privada de tipo entero llamada contador que será utilizada para hacer una condición

9)  regresar a la vista de diseño de workflows y arrastrar desde el toolbox una actividad del tipo whileactivity y arrastrar dentro de esta actividad otra actividad del tipo code

10) en las propiedades de la actividad del tipo whileactivity seleccionar la propiedad Condition y seleccionar el tipo Declarative Rule Condition, expandir la propiedad condition y oprimir el botón con los tres puntos para qué se abra el editor de condiciones

 

11) en el editor de condiciones oprimir el botón new  y escribir Contador  < 5, y presionar el botón ok del editor de condiciones y de la ventana previa

12) regresar a la vista de diseño de workflows y dar doble clic dentro de la actividad code que se encuentra dentro de la misma y escribir el siguiente código

 

Contador += 1

Console.WriteLine("Soy una actividad repetitiva")

 

13) ejecutar la aplicación y verificar que la actividad se repita.

Como podemos ver WF es una tecnología muy atractiva. Quizá no le encuentren mucho chiste ahorita por el ejemplito tan sencillo que hicimos, pero siempre es bueno empezar por el comienzo. Estaremos hablando con mucho más detalle más adelante

Pues espero que les haya gustado esta primera entrega de WF, que fue mas que nada teórica y para contextualizar en que nos ayuda WF. Vienen varias más. Cualquier duda o comentario, por favor postéenlo en los foros o mándenme un mail a starcatchingboy@gmail.com

Miguel A. Morán B.
Microsoft MVP Visual Developer
 

Posted: 05-04-2007 7:43 PM por Bichi | con no comments |