Miguel Angel Morán

Machines take me by surprise with great frequency...
Webcasts en MSDN de Microsoft

Los invito cordialmente a que me acompañen a los siguientes webcasts que estaré dando para el MSDN de Microsoft.

Webcast MSDN: Base de datos de SQL Server para la Plataforma de datos – Transite del servidor a los dispositivos y a la nube

miércoles, 08 de abril de 2009

Los servicios de datos basados en la nube cambiarán la forma en que usted desarrolla e implementa aplicaciones y las tecnologías SQL Server evolucionan para ayudarle a integrar soluciones impulsadas por datos que abarcan dispositivos, escritorios, servidores y la nube. Descubra la forma de usar l Marco de servicios de datos ADO.NET (nombre de código "Astoria"), SQL Server Data Services (SSDS) y Microsoft Sync Framework.

Aquí está la liga http://tinyurl.com/c8wboq

Webcast MSDN - Programar Live Services usando tecnologías distintas a Microsoft

Los bloques de construcción de Live Services exponen un modelo de programación independiente de plataformas, neutral al lenguaje y basado en protocolos. El marco se diseñó bajo el concepto de obtener-seguir para no estar atado a ninguna tecnología (Microsoft u otra) o implementación específica de plataforma.

Fecha: 23/04/2009

Y aquí está la liga:  http://tinyurl.com/d336ae

No falten, se van a poner muy buenos, los webcasts, no ustesdes… :)

Maic
MVP C#

Introducción a los lenguajes dinámicos con C# 4.0 y sus aplicaciones

 

Los lenguajes dinámicos, en pocas palabras, son la neta. Mi entusiasmo a este tipo de programación siempre ha sido evidente por el poder que representa para el programador. De ahí mi gusto siempre desenfrenado por Visual Basic, dada su implementación rudimentaria de dinamismo (el famoso late binding) y las cosas que podíamos hacer con COM desde épocas inmemorables, ya platicaremos en un momento más al respecto de ésto.

Pero vamos por partes.

En este artículo aprenderemos que son y para qué sirven estos lenguajes y trato de explicar muchos conceptos básicos (y quizá alguno no tan básico) sobre ella, además de ver la implementación ya posible con C# 4.0

Espero que este artículo sea del gusto de todos ustedes y que sirva como punto de inicio para prepararnos (o reencontrarnos) con las delicias de la programación dinámica.

Antecedentes:

Anders Helsberg anunció C# 4.0 en el PDC 2008, C# 4.0 representa un paso más en la evolución de C#. Los milestones, o las características principales de C# son los siguientes

· C#1.0: La primera versión de nuestro querido lenguaje de programación creado específicamente para la plataforma .NET

· C#2.0: Más funcionalidad con tipos de datos genéricos, tipos anulables, métodos anónimos y algunas otras delicias sintácticas

· C# 3.0: Inicializadores de objetos, expresiones lambda, árboles de expresiones, métodos de extensión entre otras cosas, todo esto para lograr desde luego el soporte a LINQ.

· C#4.0: Programación dinámica y quizás la posibilidad de compilador como servicio (quizás porque Microsoft no ha anunciado nada hasta el momento, quizás sea parte de C#4.0 o quizás tengamos que esperar hasta la siguiente versión)

Así que lo más destacado de la versión 4.0 de C# es la posibilidad de manejar tipos dinámicos, lo cual explicaremos a continuación

¿Qué es un lenguaje dinámico?

Un lenguaje dinámico, en pocas palabras es un lenguaje que en tiempo de ejecución realiza cosas que tradicionalmente se harían en tiempo de compilación. Entre estas cosas tenemos:

1) Parseo (Análisis) de expresiones: Es poder enviar al lenguaje generalmente una cadena y que el tiempo de ejecución lo interprete como código ejecutable en el acto. Si les viene a la mente la función Eval() de Javascript, están en lo cierto, es exactamente eso.

2) Verificación de tipos de datos. Si en C# tratamos de sumar perros, con enteros, con cadenas y con gatos, el compilador nativamente nos daría un error en tiempo de compilación la mayoría de las veces (digo la mayoría porque en tiempos de las primeras versiones de C# cuando no había tipos genéricos podíamos hacer operaciones de este tipo y recibir el tronido hasta que el programa ya estaba corriendo juar juar). Con un lenguaje dinámico podemos definir las reglas para manejar tipos de datos heterogéneos. Además es posible hacer el binding de un objeto a una variable

3) Redefinición y alteración a los tipos: En los lenguajes tradicionales (no dinámicos) una vez que hemos definido un tipo es imposible cambiarlo. Sí sí claro algunos de ustedes dirán que no es necesario toda vez que puedo definir contratos mediante interfaces y clases abstractas y meter cualquier tipo de información en estas plantillas y así ya queda, pero recordemos: Las interfaces y las clases abstractas no son en runtime, es decir, no puedo alterar el contrato una vez definido, además de que debo escribir una gran bola de código para definir estas estructuras. Lenguajes como Phyton o Ruby permiten nativamente esto, y C# 4.0 también, como lo veremos en éste artículo

4) Emisión de bloques ejecutables: Esto se refiere a código que genera código. Se implementa mediante closures y funciones de orden superior, esto es parcialmente posible en C# mediante funciones de orden superior y expresiones lambda (ver más en mi artículo publicado en la revista Software Gurú)

¿Y para qué sirve todo esto? Los lenguajes dinámicos facilitan enormemente la labor del programador haciéndolo más productivo, ayudándolo entre otras cosas con:

· Permiten la carga de tipos de datos desde estructuras externas (como por ejemplo un archivo en XML o en JSON) y usar sintaxis nativa de atributo para asignar o leer información

· Permiten usar datos sin necesidad de definir contratos (las interfaces, precisamente son el paliativo de la estaticidad de C#)

· Fomentan el bajo acoplamiento (No tengo que tener referencias a bibiliotecas de tipo, que si cambian, me hacen recompilar el código)

· Eliminan las bibliotecas de tipos y/o intefaces

· Eliminan las referencias circulares (molesto y horrible dolor de cabeza)

· Disminuyen la cantidad de código requerido

· Evalúan en tiempo real fórmulas y código (por ejemplo para los molestos cálculos de nómina, dado que las leyes fiscales cambian constantemente)

· Facilitan la creación de alfabetos finitos con significado para nuestro programa

· Modifican el comportamiento de un proceso o flujo de trabajo en tiempo real sin necesidad de recompilar

· Dan la posibilidad de generar un “polimorfismo extremo” dirigido por los datos, esto es, permiten que un tipo de datos mute (o sea que se le agregue comportamiento o estado en tiempo de ejecución) en cualquier momento

· Facilitan la metaprogramación, es decir código que genera o manipula código, por ejemplo yo podría permitirle a un usuario describir como es un tipo de dato y en tiempo de ejecucución tener un programa que manipule o lea esa información

Como podrán ver son bastantes las ventajas y puedo seguir echando choro acerca de todo lo que se puede hacer con un lenguaje dinámico, pero también hay que decirlo, también tienen sus desventajas como exponer nuestro programa a errores en tiempo de ejecución, herramientas de programación más pobres (adiós Intellisense), menor desempeño en la aplicación por la chamba adicional que le damos a la computadora de inferir y hacer casts en tiempo de ejecución. Hay que poner en la balanza si estos problemas no superan al beneficio de usar los lenguajes dinámicos.

Existen diferentes implementaciones de dinamismo en lenguajes como Ruby, Phyton y hasta el mismo Visual Basic y COM con su capacidad para hacer late binding, sin embargo aquí nos concentraremos en aprender cómo funcionarán estos conceptos en C#.

La línea de un lenguaje de programación estático y dinámico no es muy clara, los lenguajes estáticos (el CLR de .NET en general) fue creado para ser fuertemente tipado, sin embargo tiene alternativas para generar “cierto” dinamismo. La herramienta principal de .NET para esto es la reflección (Reflection) en el namespace System.Reflection. Este namespace contiene clases para conocer en tiempo de ejecución los tipos de datos, es más, es posible generar métodos dinámicos escribiendo directamente los opcodes del IL (Intermediate Language), sin embargo esto es sumamente complejo de lograr toda vez que tendríamos que aprender IL además de existir muchas limitantes, lo que trato de decir es que no creo que haya un lenguaje totalmente dinámico ni otro totalmente estático, cada lenguaje tiene su propia implementación en mayor o menor medida para realizar las tareas que enumeré en párrafos anteriores.

Un poco de historia en la plataforma Microsoft sobre los tipos dinámicos

Como escribí en los renglones superiores, esto de no ser tan estricto en la tipificación de los datos no es nuevo en la plataforma Microsoft.

Quienes programamos alguna vez con COM o con Visual Basic 6.0 recordaremos código como el siguiente:

 

'VB 6.0/VBScript 
 
Dim lobjUnObjeto 
 
Set lobjUnObjeto = CreateObject("ADODB.Connection") 

Lo que sucede con el código anterior es que la función CreateObject devuelve una referencia a un objeto COM. Todo objeto COM debe tener implementada la interface IDispatch (que a su vez hereda de IUknown y que son las interfaces básicas para hacer que un objeto tenga la capacidad de utilizarse en COM) Veremos en un rato cómo se parece el modelo a lo que actualmente (o próximamente) tendremos en C#.

Una vez que utilizábamos una sintaxis semejante a la anterior podíamos fácilmente mandar a llamar a cualquier método o atributo con la sintaxis del puntito

 

lobjUnObjeto.Metodo1
lobjUnObjeto.Atributo1 

Tener una situación similar en C# 3.0 o anteriores es imposible, toda vez que siempre es necesario agregar una referencia a y en tiempo de compilación conocer previamente el tipo de datos para poder hacer cualquier llamada a un método o atributo.

Pues que creen... En C# 4.0 YA ES POSIBLE OTRA VEZ jeje. De hecho éste artículo el artículo se iba a llamar “Y Visual Basic siempre tuvo razón”, pero seguramente iba a herir varias susceptibilidades, así que le cambié el nombre. Fuera de coto, la posibilidad de utilizar sintaxis dinámica en C#, desde mi humilde punto de vista hace a C# un lenguaje de programación multiparadigma real, con capacidad para utilizar el paradigma estructurado, orientado a objetos, funcional y dinámico. Esto es una excelente noticia para todos nosotros que día a día nos enfrentamos a pantallas llenas de código en C#.

Algo que me gustaría aclarar es que el código fuertemente tipeado debe de prevalecer, el hecho de que C# tenga estas nuevas funcionalidades no significa que debemos olvidar todo lo que hemos aprendido hasta el momento sobre las buenas prácticas de la programación orientada a objetos y componentizada (interfaces, contratos, mecanismos de publicación – suscripción etc.), yo creo que como deberíamos de ver estas alternativas exactamente como eso, simplemente como una serie de alternativas que nos darán mayor productividad y facilidad al programar, pero no debemos de perder el foco y caer en el libertinaje haciendo código dinámico y olvidar de plano la tipificación.

Todo se resume en una máxima: Usemos siempre fuerte tipificación, a menos de que sea absolutamente necesario, usemos tipos dinámicos.

Basta pues de todo esto y concentrémonos ahora en ver la implementación básica de dinamismo en C# 4.0.

El tipo de datos dynamic

C# 4.0 introduce el tipo de datos dynamic, que no es más que una manera alternativa de decirle al compilador que NO verifique el tipo de datos en tiempo de ejecución. Según los arquitectos de C# la palabra dynamic representa un objeto estáticamente tipado para ser dinámico.

El código se ve así:

 

 
   1: //C# 4.0
   2: dynamic variable = 1; 

En pocas palabras, y sin tanto choro, lo que sucede es que estamos declarando un objeto del tipo objeto pero como mencionamos esta implementación permite dejar la verificación del tipo (el binding) hasta el tiempo de ejecución

¿Ustedes dirán y eso no es lo mismo que var?  La respuesta es ¡No!, son dos cosas completamente diferentes, veamos el siguiente ejemplo

   1: //C# 4.0
   2: dynamic objeto = 1;
   3: var objeto2 = 1;

En la línea 2, internamente lo que sucede es que se asigna el tipo de datos objeto  el cual se conocerá hasta el runtime y en la línea 3 quien infiere el tipo de datos es el compilador y antes de ejecutarlo se conoce el tipo, por lo que sigue siendo un tipeado estático.

Internamente esta funcionalidad se monta sobre una pieza de software que ya tiene un ratito entre nosotros que es el DLR.

El DLR

El DLR o Dynamic Language Runtime que se monta sobre el CLR, es decir, no lo reemplaza ni mucho menos, es simplemente código adicional que sirve para facilitarnos la vida a la hora de hacer las cosas dinámicas. Físicamente vive en un ensamblado llamado Microsoft.Scripting y proporciona clases que facilitan en gran medida la utilización de lenguajes dinámicos.

El DLR tiene como funcionlidad proveer funciones básicas de dinamismo, de la misma manera que en COM y VB 6.0 lo hacía la interface IDispatch y la función CreateObject, sin embargo y a diferencia de COM, el CLR tiene funciones de optimización para hacer que el código sea mucho más eficiente, toda vez que tiene soporte para caché de objetos, además de permitir la interoperabilidad de diferentes lenguajes de programación dinámicos como IronPython o IronRuby.

Vayamos ahora a aplicaciones reales de esta tecnología

Bajo acoplamiento con C# 4.0

Como lo he mencionado creo que en la mayoría de los posts que he hecho, es básico y conocido por todos que debemos fomentar la ya veterana la programación basada en componentes, al menos en tres capas, capa de usuario, regla de negocio y capa de persistencia o acceso a base de datos. Generalmente hay una dependencia de composición o agregación entre dichas capas, o sea agregamos una referencia desde la capa de usuario para que vea a los tipos y funciones de la Regla de Negocio y a su vez esta tiene una referencia a la capa de datos.

El problema con C# es que si queremos seguir un modelo semejante en C#, además debemos definir bibliotecas de interfaces para que la capa de datos conozca las entidades porque las entidades (clases especializadas en la persistencia de datos) generalmente deben persistir en una regla de negocio, o bien en el caso de que queramos realizar inyección de dependencias sucede exactamente lo mismo.

El tipo de datos dynamic permite una manera sencilla de hacer esto aun mas simple de lo que ya es. Veamos como

Lo primero que vemos es la capa de usuario (en este caso una vil formita con dos botones), uno que acumula datos en un diccionario y el otro que invoca a la Regla de negocio con la cual tiene dependencia, éste código es perfectamente legal en C# 3.0 porque no hay nada nuevo y aun no usamos la palabra clave dynamic. Examinemos pues el código

 

   1: ///C# 4.0///////////CAPA DE USUARIO//////////////////////////
   2: using System;
   3: using System.Collections.Generic;
   4: using System.ComponentModel;
   5: using System.Data;
   6: using System.Drawing;
   7: using System.Linq;
   8: using System.Text;
   9: using System.Windows.Forms;
  10: using Rdn.Entidades;
  11:  
  12: namespace Usr
  13: {
  14:     public partial class Form1 : Form
  15:     {
  16:         //Aqui guardare los datos de las personas
  17:         List<Persona> Personas = new List<Persona>();
  18:  
  19:         //PASO 1-------------------------------------------------------------------------
  20:         //Este botón lo único que hace es agregar entidades persona al diccionario Personas
  21:         private void button1_Click(object sender, EventArgs e)
  22:         {
  23:           //Invoco a la entidad Persona de regla de negocio (Rdn) y la lleno
  24:             Rdn.Entidades.Persona unaPersona = new Rdn.Entidades.Persona() { 
  25:                 Id=Personas.Count+1, AMaterno="Perez", APaterno="Perez", Nombre="Lucha" };
  26:           //Sólo agrego la entidad al diccionario
  27:             Personas.Add(unaPersona);
  28:         }
  29:  
  30:         //PASO 2 -------------------------------------------------------------------------
  31:         //Aquí doy clic cuando termino de agregar personas al diccionario
  32:         private void button2_Click(object sender, EventArgs e)
  33:         {
  34:             //Instancio la Rdn
  35:             var ReglaNegocio = new Rdn.Rdn();
  36:             //Invoco el método de la Rdn y le paso el diccionario lleno de Personas
  37:             ReglaNegocio.AgregarPersonas(Personas);
  38:  
  39:         }
  40:  
  41:  
  42:         public Form1()
  43:         {
  44:             InitializeComponent();
  45:         }
  46:        
  47:     }
  48: }

Posteriormente veremos lo que hay en la RDN. En este caso y para hacer menos verboso el código esta RDN realmente no hace nada (recordemos que las capas de negocio tienen como función principal la orquestacíón de los procesos), sino que le avienta el diccionario a la capa de acceso a datos (persistencia). Igual que en el código anterior, éste código NO tiene nada novedoso.

 

   1: //C# 4.0 ////////////////////CAPA DE REGLA DE NEGOCIO
   2: using System;
   3: using System.Collections.Generic;
   4: using System.Linq;
   5: using System.Text;
   6:  
   7: namespace Rdn
   8: {
   9:     public class Rdn
  10:     {
  11:         public void AgregarPersonas(List<Entidades.Persona> pobjPersonas)
  12:         {
  13:             //Instancio a la capa de persistencia y le paso el mismo objeto recibido
  14:             var BaseDatos = new Abd.Abd();
  15:             BaseDatos.Persistir(pobjPersonas);
  16:         
  17:         }
  18:     }
  19: }

Aquí tienen la definición de la clase Persona

   1: namespace Rdn.Entidades
   2: {
   3:    
   4:     public class Persona
   5:     {
   6:       public int Id;
   7:       public string APaterno;
   8:       public string AMaterno;
   9:       public string Nombre;
  10:       public string Telefono;
  11:     
  12:     }
  13: }

Por fin llegó el momento de utilizar la palabra clave dynamic. La usaremos en este ejemplo precisamente en la capa de persistencia.

   1: //C# 4.0 CAPA DE ACCESO A DATOS CON dynamic
   2: using System;
   3: using System.Collections.Generic;
   4: using System.Linq;
   5: using System.Text;
   6: using System.Windows.Forms;
   7: using System.Diagnostics;
   8:  
   9: namespace Abd
  10: {
  11:     public class Abd
  12:     {
  13:         //Recibo la entidad persona, pero como podemos ver NO CONOZCO EL TIPO!!!
  14:         //sólo lo declaro como dynamic y el código es completamente válido
  15:         public void Persistir(dynamic pdynObjeto)
  16:         {
  17:  
  18:             foreach (dynamic Item in pdynObjeto)
  19:             {
  20:                 //Aqui se pone el código para persistir en bdd, en este
  21:                // ejemplo solo lo mando a la ventana de debug
  22:                 Debug.Write(Item.APaterno + " "  + Item.AMaterno+ " " +Item.Nombre);
  23:                 Debug.Write(Item.Id.ToString());
  24:  
  25:             }
  26:           
  27:         }
  28:  
  29:     }
  30: }

Como podemos ver no tuve que hacer interfaces, ni bibliotecas de tipos. El compilador permite la compilación sin ningún problema y descubre en tiempo de ejecución las propiedades (en teste caso APaterno, AMaterno etc, de los datos que le llegan.) jaja al más puro estilo de VB 6.0. Quiero señalar que en un contexto controlado esta implementación no es la óptima, sin embargo, es útil cuando queremos implementar arquitecturas dirigidas por los datos.

¿Qué más se puede hacer con C# 4.0?

Generación de atributos dinámicos en tiempo de ejecución.

Examinemos el siguiente código válido en C# 4.0, creo que habla por sí solo...

   1: //C# 4.0
   2: private void button3_Click(object sender, EventArgs e)
   3:        {
   4:            //Creo un diccionario de atributos que se agregarán dinámicamente
   5:            //en tiempo de ejecución a una clase que se llama PerroDinamico
   6:            Dictionary<string, object> AtributosDinamicos = new Dictionary<string, object>();
   7:            AtributosDinamicos.Add("Color", "Verde");
   8:            AtributosDinamicos.Add("Raza", "Chihuahua");
   9:            AtributosDinamicos.Add("Edad", 7);
  10:            //Defino tipo dinámico
  11:            dynamic UnPerroDinamico = new PerroDinamico(AtributosDinamicos);
  12:            //Invoco a los atributos con sintaxis estándar de atributo
  13:            //como si 'realmente' existieran, cosa que no se podía hacer antes
  14:            //porque este código hubiera lanzado una excepción
  15:            MessageBox.Show(UnPerroDinamico.Raza);
  16:            MessageBox.Show(UnPerroDinamico.Edad.ToString());
  17:           
  18:        }

El msgbox evidentemente muestra "Chihuahua" y 7. Esta clase llamada PerroDinamico hereda de.

Veamos su implementación:

   //C# 4.0

1: public class PerroDinamico : System.Scripting.Actions.Dynamic

//Clase con la interface IDynamicObject implementada

   2:     {
   3:         Dictionary<string, object> DiccionarioAtributos= new Dictionary<string,object>();
   4: //Recibo el diccionario con los atributos
   5:         public PerroDinamico(Dictionary<string, object> pAtributos)
   6:         {
   7:             DiccionarioAtributos = pAtributos;
   8:         }
   9:  
  10:         public PerroDinamico()
  11:         {
  12:             throw new NotImplementedException();
  13:         }
  14:  
  15: //Estos métodos proveen la implementación de los atributos dinámicos, en este caso
  16: //no se realiza implementación adicional
  17:         public override object GetMember(GetMemberAction action)
  18:         {
  19:  
  20:             return DiccionarioAtributos[action.Name];
  21:          
  22:         }
  23:  
  24:         public override void SetMember(SetMemberAction action, object value)
  25:         {
  26:          
  27:             DiccionarioAtributos[action.Name] = value.ToString();
  28:            
  29:         }
  30:             
  31:            
  32:         }

Con esto podemos comprobar que el dinamismo con el DLR de .NET es bastante poderoso, toda vez que es posible implementar cualquier tipo en forma dinámica, y compartir la implentación independientemente del lenguaje.

Como conclusión puedo decir que estoy muy emocionado con todo esto, nos permitirá hacer cosas que antes eran, si no imposibles, muy talachudas de hacer y de nuevo, los invito a que cuando esto salga no lo usemos en demasía, solamente para cuando sea estrictamente necesario.

Pues a grandes rasgos esta fue una explicación somera del funcionamiento del dinamismo en C#. Espero que les haya gustado, cualquier duda o comentario no duden en externarlo poniendo un reply.

Gracias y los invito a que vean esto, es la entrevista de Anders Helsberg sobre las motivaciones que llevaron al equipo de C# a implementar estas cosas, se las recomiendo especialmente a los que no están muy convencidos de esta nueva flexibilidad:

mms://mschnlnine.wmod.llnwd.net/a1809/d1/ch9/4/2/3/2/4/4/CSharpFourQuestionsAndReasons_s_ch9.wmv

Un abrazo a tod@s y ¡Feliz Codificación!

Colaboración en Software Gurú: Más allá de los objetos, C# funcional

Como les comenté en posts anteriores sigo medio obsesionado con estas cosas de la programación alterna (dinámica, declarativa, funcional etc.) Les dejo un link para que visiten el sitio de la revista Software Gurú donde salió publicado un artículo mío denominado "Más allá de los objetos, C# como lenguaje funcional", en la edición de Mayo-Julio de 2008.

¡Cómprenla!

El link es:

http://www.sg.com.mx/content/view/713

Traté de explicar con varios ejemplos de como usar C# con características funcionales, me faltó un buen pero ps el espacio era limitado, igual acá profundizamos más en algunos posts posteriores.

El código fuente es este, abran una nueva aplicación de consola con Visual Studio 2008 (.NET Framework 3.5) y denle Copy Paste al siguiente código.

Lean el artículo, espero que les guste y cualquier duda me la pueden hace llegar a través de los foros de este sitio o a mi mail starcatchingboy@gmail.com

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using System.Linq.Expressions;
 
namespace Devworx.SG
{
 
    class Program
    {
             
        //Función de orden superior
        public static Func<int,int> ConvertirMoneda(string pstrMoneda) {
        return (int pintCantidad) => 
        { return pstrMoneda == "EURO" ? pintCantidad * 15 : pintCantidad * 10;  };
       }
 
 
        static void Main()
        {
 
          //Expresiones lambda CUBO
            
            Func<int, int> lintResultado = (pintNum) => { return pintNum * pintNum; };
            Console.WriteLine(lintResultado(3));
 
 
            //Inferencia de tipos
            var lobjMsg= "Hola";
            lobjMsg.GetType().ToString();
            Console.WriteLine("El tipo inferido es " + lobjMsg.GetType().ToString());
 
            //Invocación a Función de orden superior con parámetro euro
 
            var lobjConvertidor = ConvertirMoneda("EURO");
            Console.WriteLine("La FOS regresa " + lobjConvertidor(20) + " si se le invoca con euro");
 
            //Invocación a Función de orden superior con parámetro dolar
 
            lobjConvertidor = ConvertirMoneda("DOLAR");
            Console.WriteLine("La FOS regresa " + lobjConvertidor(20) + " si se le invoca con dolar" );
 
 
            //Sumatoria sin for, al modo funcional
            Console.WriteLine("La sumatoria de 100 es: " + Enumerable.Range(1, 100).Sum());
 
            Console.ReadKey();
   
        }
 
    }
}

Un abrazo a tod@s y
¡Feliz Codificación!

Reflexiones sobre paradigmas de programación

Me encuentro en el aeropuerto de Seattle esperando mi vuelo rumbo a casita, después de una semana sumamente enriquecedora (debo decir, mucho más de lo que yo esperaba) asistiendo al MVP Summit 2008. Creo que fue un evento impresionante, que me sorprendió la manera en la que habló Steve Ballmer y que es extraordinario estar en medio de los equipos de producto de Microsoft (particularmente estuve con los equipos de VB y C#)  y en este post me gustaría platicar un poco respecto a mis impresiones.

Desafortunadamente (o afortunadamente) hay un contrato NDA (Non Disclosure Agreement) firmado entre Microsoft y los MVPs que impide comentar detalles y los ejemplos concretos de lo que hemos visto. Sin embargo quisiera compartir algunas reflexiones en estos tiempos de cambio en los que vivimos.

Mi área de interés en esta parte de mi carrera profesional es la teoría de lenguajes de computación, me enoja un poco tener tanto trabajo y no poder dedicarme más a este apasionante tema, sin embargo en tiempecitos he estado investigando y aprendiendo cosas muy interesantes durante los últimos meses, es un tema que me apasiona, quizás como ningún otro en la informática y en este Summit tuve la oportunidad de asistir a pláticas exactamente de estos tópicos, así que con algo de tristeza renuncié a pláticas de Silverlight, de  SQL, de SharePoint  y hasta de LINQ, y enfoqué todo mi track de sesiones exclusivamente en el área de los lenguajes “puros”, lo cual fue la mejor decisión que pude haber tomado. Estas sesiones me hicieron reafirmar algunas creencias que tengo y modificar otras y las enumeraré a continuación

1) Cosas que siempre he dicho que reafirmé

a)  Seguiremos en el camino de la computación “multiparadigma”:

Es bueno ser teórico para programar, me gusta mucho conocer gente que le guste adherirse a una escuela o patrón de diseño, yo soy el principal defensor de la teoría al programar, sin embargo hay que decir que ni VB ni C# son lenguajes puramente orientados a objetos, y mucho menos con la introducción de Visual Studio 2008, con la cual los lenguajes adquiririeron elementos funcionales. Microsoft seguirá por ese rumbo para efectos de aumentar las opciones y la productividad del programador, de cualquier tipo y de cualquier escuela. Los lenguajes evolucionan, deben hacerse más fáciles, mejores y con muchas opciones. Java alguna vez presumía de que era un lenguaje de programación “fácil” y en sus primeras versiones se jactaba de que en el lenguaje solamente se podían hacer las cosas de 2 o 3 maneras distintas cuando en otros lenguajes como C++ lo mismo se podía hacer de 20 formas diferentes. Pues a Java se le cayó esa premisa y conforme ha ido evolucionando (es impresionante que hasta hace algún tiempo java no tenia foreach :P) ha tenido que cambiar e introducir cosas como genéricos  y muchas construcciones que le han dado flexibilidad al lenguaje. Lo mismo ha pasado con .NET, sigue en constante evolución y creo que esto es el camino que tendremos en el corto y mediano plazo.

Me saca de onda un poco que a estas alturas del partido haya MVPs en C# que sigan diciendo que var es malo o que se oponen a la evolución natural de los lenguajes, no debería de ser, los lenguajes entre más opciones nos den, se vuelven más flexibles, es función del arquitecto de software elegir un paradigma, patrón  y arquitectura para generar mejor software.

b) Los lenguajes open source seguirán ahí:

Microsoft no tiene interés es “competir” frontalmente o posicionar a C# o a VB como una alternativa a lenguajes como Ruby, al contrario, la idea es integrar cada vez más lenguajes de programación heterogéneos que sean compatibles con el CLR y con el DLR (Dynamic Language Runtime). Microsoft está muy comprometido con la comunidad open source y la integración de nuevos lenguajes más específicos para determinadas tareas como F#, Ruby, Phyton etc. será (es) ya un hecho dentro de Microsoft.

c) Los lenguajes dinámicos (y el late binding) NO son malos, si se usan con precaución:

Lo tengo que aceptar: Soy FANÁTICO de los lenguajes que aceptan late binding (léase Visual Basic),  siempre lo he sido y seguramente lo seré. Microsoft está haciendo grandes esfuerzos por encontrar una solución que balancee la seguridad del static typing y la flexibilidad del dinamismo y/o late binding. (Por cierto, la diferencia principal del dinamismo al Late Binding es muy sutil podemos decir que un lenguaje dinámico no es más que uno con latebinding con esteroides, y los esteroides son precisamente la creación de un tipo nuevo en compile time (antes de su ejecución), cosa que no sucede con late binding, donde se conoce el tipo hasta la ejecución del mismo y este se encuentra boxeado en un tipo general (que no es lo mismo que genérico) llamado object)

Seguramente un servidor (yo) y DevWorx estaremos haciendo algo al respecto. Allá afuera pasan muchas cosas y no nos vamos a quedar sin participar. El tema de los lenguajes dinámicos es extremadamente apasionante y lo considero como la orientación a objetos real, más humana, más antropomorfizada, es decir, ¿para qué hacer casts y tener bibliotecas de interfaces? Podemos, sin duda alguna generar una real orientación a objetos mediante un polimorfismo llevado al extremo y la programación dinámica es un pilar fundamental para lograrlo. Les recomiendo mucho leer esto: http://pico.vub.ac.be/~wdmeuter/RDL04/papers/Meijer.pdf 

 

2) Cosas que siempre he dicho y que tendré que rectificar

1) He dicho en varios posts que C# se está convirtiendo en un lenguaje funcional y VB en un lenguaje dinámico: Aunque C# 3.0 es más funcional que VB 9.0, en el futuro inmediato pareciera ser que ambos lenguajes  seguirán teniendo un soporte similar a tecnologías dinámicas y funcionales respectivamente lo cual lejos de ser malo, es excelente, ambos lenguajes están adquiriendo las capacidades necesarias para seguir estando en la vanguardia y  que se pueda programar en ellos cualquier software por complejo que parezca.

En fin, la semana del Summit se acabó y mis proyectos en me esperan apilados, tengo que programar teléfonos celulares, dar cursos de SharePoint (arg!), no es lo que más me gusta, pero ni modo la chamba es la chamba y hay que verla de la mejor manera posible. Espero pronto postear varios posts que tengo empezados pero no terminados.

 

 

2D y animaciones con WPF


Existen muchas razones por las cuales una aplicación podría tener la capacidad de dibujar en pantalla y contenido visual como rectángulos, elipses, triángulos, círculos etc. WPF incluye una gran cantidad de funcionalidad para tener este tipo de gráficos y realmente existe una mejora significativa al dibujar con esta tecnología respecto a tecnologías previas como GDI+.

Shapes

Las Shapes (formas) son primitivas que nos permiten dibujar automáticamente algunas formas básicas y muy usadas en la definición de gráficos. La ventaja de utilizar Shapes es que incluyen todo lo necesario para definir la presentación y aspecto visual en las figuras sin necesidad de componer árboles de objetos complejos.

Las Shapes definidas por WPF se encuentran en el siguiente listado

Shape (Forma)

 

 

Ellipse

Dibuja círculos y elipses de circunferencia fija si se le especifican los atributos Width y Height o variable de acuerdo a su objeto contenedor si no se le especifican dichos atributos.

Line

Dibuja una línea dados dos puntos. Es posible especificar la presentación de la línea utilizando los atributos Stroke, StokeThikness entre otros.

Rectangle

Dibuja rectángulos y cuadrados y tiene la capacidad de modificar la presentación de las esquinas y el trazado de las líneas del rectángulo

Polyline

Dibuja rectas continuas

Polygon

Dibuja rectas continuas y añade un segmento de recta adicional para cerrar la figura.

Path

Dibuja líneas, curvas y en general cualquier caso siguiendo un conjunto de instrucciones especificadas mediante XAML o una notación especial

 

Ellipse:

El siguiente código muestra la manera de dibujar dos elipses. La primera elipse tiene especificados los atributos Width Y Height por lo que al momento de cambiar de tamaño la ventana el dibujo de la elipse permanecerá inamovible mientras que en la elipse llamada MiElipseFija ocurrirá un redimensionamiento toda vez que hereda el tamaño especificado por su objeto contenedor

XAML

<Ellipse Margin="68,47,82,86" Width="50" Height="50" Name="MiElipseMovible" StrokeThickness="5" Stroke="Red" /> <Ellipse Margin="68,47,82,86" Name="MiElipseFija" Stroke="Blue" StrokeThickness="5"></Ellipse>

 

El resultado es el siguiente:

Para definir que una elipse será rellenada con determinado color es posible utilizar el atributo Fill.

Line:

Las líneas son uno de los elementos visuales más utilizados en las aplicaciones. Para definir una línea es necesario indicar los puntos origen y destino a través de los cuales se dibujará el segmento de recta. Para definir estos puntos hacemos uso de los atributos X1, Y1 para definir el primer punto y X2, Y2 para definir el segundo punto. El ejemplo siguiente muestra cómo definir dos líneas.

XAML

<Line X1="0" Y1="0" X2="300" Y2="280"

Stroke="Blue" StrokeThickness="14" />

 <Line X1="0" Y1="260" X2="280" Y2="0"

StrokeThickness="14">

<Line.Stroke>

<ImageBrush ImageSource="C:\Revista USERS\2D\fondo.jpg" />

</Line.Stroke>

</Line>

La primera línea define para el atributo Stroke un color fijo mientras que la segunda línea utilizada un elemento ImageBrush para definir el trazado de dicha línea utilizada como fondo una imagen especificada en un archivo el resultado del código anterior es el siguiente.

Polyline

Este objeto representa una colección de líneas. Las líneas son definidas a través de la propiedad Points donde se definen los puntos por los cuales pasará el conjunto de líneas. De la misma manera que sucede con el objeto Line, podemos establecer el formato del trazado de la línea mediante los atributos Stroke, StrokeThikness entre otros. Además es posible utilizar el atributo Fill para llenar el área por la que pasan las líneas.

El código a continuación genera un triángulo:

XAML

<Polyline Points="100,100 200,40 300,100 104,100" Stroke="Black"

StrokeThickness="10" Margin="0,50" StrokeLineJoin="Round" Fill="Red"/>

 

Resultado

 

 

Polygon

La funcionalidad entre Polyline y Polygon es prácticamente la misma. La única diferencia que existe entre ambos objetos es que Polygon automáticamente añade una línea entre el primero y el último punto y de esta manera se genera una figura cerrada. A continuación mostramos el mismo ejemplo anterior utilizando ahora para ver la diferencia entre uno y otro

XAML

<Polygon Points="100,100 200,40 300,100 170,100" Stroke="Black"

StrokeThickness="10" Fill="Red" Margin="0,50" StrokeLineJoin="Round" />

 

Path

Con la clase Path es posible dibujar prácticamente cualquier trazo. A diferencia de las figuras vistas anteriormente, con éste objeto es posible dibujar inclusive curvas y rectas figuras abiertas y figuras cerradas, para eso, hace uso de la propiedad Data que tiene una notación que permite definir los movimientos, las curvas y las líneas mediante una notación sintaxis que se detalla a continuación.

Shape (Forma)

 

 

M

Define el punto inicial de una figura

L

Define una recta a partir de la posición actual y hasta el punto especificado

V, H

Definen una línea vertical u horizontal respectivamente

C, Q, S, T

Definen curvas de Bezier (cúbicas, cuadráticas, cúbicas suaves y cuadráticas suaves respectivamente)

A

Dibuja un arco elíptico

Z

Cierra la figura actual

 

El siguiente ejemplo muestra como utilizar el objeto Path

XAML

<Path Stroke="Black" Fill="Blue">

<Path.Data>

<PathGeometry Figures="M 10,100 C 10,300 300,-100 300,100 " />

</Path.Data>

</Path>

 

El código previo genera la siguiente salida:

 

 

Cabe señalar que cuando es necesario dibujar una figura compleja no es muy práctico editar a mano el XAML; es por eso que podemos hacer uso de herramientas especializadas en diseño gráfico como Expression Blend que automáticamente genera el código requerido para representar los trazos y dibujos creados en la herramienta misma.

A continuación un ejemplo de código generado automáticamente con Expression Blend utilizando su herramienta Pencil.

XAML

<Path Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left" Margin="154.833,0,0,121.5" VerticalAlignment="Bottom" Width="3" Height="59" Data="M155.33333,264 C155.76028,283.21278 157.33333,302.47815 157.33333,322"/>

 <Path Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left" Margin="189.5,0,0,122.167" VerticalAlignment="Bottom" Width="1.667" Height="59" Data="M190,263.33333 C190,282.62739 190.66667,301.97267 190.66667,321.33333"/>

 <Path Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left" Margin="156.833,0,0,152.167" VerticalAlignment="Bottom" Width="36.334" Height="2.503" Data="M157.33333,291.33333 C168.83269,289.12798 180.58536,290 192.66667,290"/>

 <Path Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left" Margin="198.432,0,0,127.415" VerticalAlignment="Bottom" Width="29.565" Height="27.752" Data="M214.66667,290 C204.25621,291.76448 202.49111,287.14113 200,303.33333 198.87719,310.63157 195.22353,319.6057 213.33333,314.66667 222.48637,312.17038 240.35052,303.42693 212.66667,289.33333"/>

 <Path Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left" Margin="238.167,0,0,125.5" VerticalAlignment="Bottom" Width="1.666" Height="55.667" Data="M239.33333,263.33333 C239.33333,281.67256 238.66667,299.67663 238.66667,318"/>

 <Path Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left" Margin="246.122,0,0,127.5" VerticalAlignment="Bottom" Width="33.045" Height="34.448" Data="M276,291.33333 C265.47034,284.113 258.73579,272.80992 249.33333,298.66667 245.70658,308.64023 241.38342,318.87219 268,310 270.13969,303.58094 273.33333,299.49906 273.33333,292 268.11259,304.18173 262.25661,306.1878 278.66667,316"/>

 

Resultado:

 

 

Animaciones

WPF incluye clases que nos permiten generar animaciones de una manera muy sencilla. Dentro de WPF básicamente las animaciones se logran mediante la variación de una o varias propiedades a través del tiempo; éstas propiedades pueden ser el tamaño del objeto, su posición, el color, su opacidad etc. Cuando las propiedades cambian durante el tiempo se percibe el efecto de animación.

Para crear una animación primero debemos de definir el objeto al cual queremos animar. Durante los siguientes pasos definiremos un círculo que variará su tamaño constantemente.

1) Antes que nada definimos un círculo con el siguiente código

XAML

<Ellipse Name="MiBalon" Height="50" Width="50" Stroke="Black" StrokeThickness="5"> </Ellipse>

 

2) Posteriormente utilizaremos la propiedad Ellipse.Triggers que define el suceso o condición que dará inicio a la animación. Existen varios tipos de Triggers como el DataTrigger que especifica una condición que se debe de cumplir para realizar alguna acción. También tenemos el EventTrigger que será el objeto que utilizaremos en éste ejemplo y que se usa cuando queremos definir un conjunto de acciones en respuesta a un evento. Cuando utilizamos EventTrigger debemos definir el evento que debe de suceder para disparar la animación mediante el atributo RoutedEvent, en este caso, definiremos el evento Ellipse.Loaded es decir: En el momento de que se cargue la Ellipse se iniciará inmediatamente la animación

XAML

<Ellipse Name="MiBalon" Height="50" Width="50" Stroke="Black" StrokeThickness="5"> <Ellipse.Triggers>

 <EventTrigger RoutedEvent="Ellipse.Loaded"> </EventTrigger> </Ellipse.Triggers> </Ellipse>

 

3) Después de definir el evento utilizaremos las clases StoryBoard y BeginStoryBoard que representan una línea de tiempo y su inicio.

<Ellipse Name="MiBalon" Height="50" Width="50" Stroke="Black" StrokeThickness="5">

<Ellipse.Triggers>

<EventTrigger RoutedEvent="Ellipse.Loaded">

<BeginStoryboard>

<Storyboard>

</Storyboard>

</BeginStoryboard>

</EventTrigger>

</Ellipse.Triggers>

</Ellipse>

 

 

4) Para finalizar la creación de nuestra animación utilizaremos la clase DoubleAnimation que representa una animación que se aplica a aquellas propiedades que sean del tipo Double. De la misma manera existen diferentes tipos de animaciones como BooleanAnimation que anima propiedades del tipo Boolean, ColorAnimation que anima propiedades del tipo Color y así sucesivamente existen objetos de diferentes tipos para animar las propiedades que tienen el tipo de datos compatible con la animación. En nuestro ejemplo podemos apreciar que utilizaremos el atributo Duration que indica el tiempo en el que tiene que ser completada la animación así como los atributos From y To que representan el valor inicial y el valor final de las propiedades que serán animadas. También utilizamos el atributo RepeatBehavior que indica la cantidad de veces que será repetida la animación. El código del ejemplo completo se presenta a continuación.

XAML

<Ellipse Name="MiBalon" Height="50" Width="50" Stroke="Black" StrokeThickness="5">

<Ellipse.Triggers>

<EventTrigger RoutedEvent="Ellipse.Loaded">

<BeginStoryboard>

<Storyboard>

<DoubleAnimation Duration="0:0:01" From="50" To="250" Storyboard.TargetProperty="Width" RepeatBehavior="Forever"/>

<DoubleAnimation Duration="0:0:01" From="50" To="250" Storyboard.TargetProperty="Height" RepeatBehavior="Forever"/>

</Storyboard>

</BeginStoryboard>

</EventTrigger>

</Ellipse.Triggers>

</Ellipse>

El código anterior produce el siguiente resultado:

Además de que podemos utilizar los colores sólidos como el fondo de cualquier objeto que tenga la propiedad Background, también es posible utilizar gradientes. Estos gradientes se establecen a través de brochas (objetos que heredan del tipo Brush) y pueden ser Lineales (LinearGradientBrush) o Radiales (RadialGradientBrush).

En nuestro ejemplo añadiremos un gradiente al fondo del Grid y posteriormente lo animaremos a través de su propiedad Offset.

A continuación utilizaremos para nuestro ejemplo otro tipo de animación llamado ColorAnimation cuyo objetivo será cambiar de color el fondo (Fill) del círculo. El XAML requerido es el siguiente.

XAML

<Grid>

<Grid.Background>

<LinearGradientBrush>

<GradientStop Color="Blue" Offset=".0"></GradientStop>

<GradientStop Color="Lime" Offset=".99"></GradientStop>

</LinearGradientBrush>

</Grid.Background>

<Grid.Triggers>

<EventTrigger RoutedEvent="Grid.Loaded">

<BeginStoryboard>

<Storyboard>

<DoubleAnimation Duration="0:0:02" From="0.0" To=".99" Storyboard.TargetProperty="(Grid.Background).(LinearGradientBrush.GradientStops)[0].(GradientStop.Offset)" RepeatBehavior="Forever" />

</Storyboard>

</BeginStoryboard>

</EventTrigger>

</Grid.Triggers>

<Ellipse Name="MiBalon" Height="50" Width="50" StrokeThickness="5">

<Ellipse.Fill>

<SolidColorBrush Color="Transparent"></SolidColorBrush>

</Ellipse.Fill>

<Ellipse.Triggers>

<EventTrigger RoutedEvent="Ellipse.Loaded">

<BeginStoryboard>

<Storyboard>

<DoubleAnimation Duration="0:0:02" From="50" To="200" Storyboard.TargetProperty="Width" RepeatBehavior="Forever"/>

<DoubleAnimation Duration="0:0:02" From="50" To="200" Storyboard.TargetProperty="Height" RepeatBehavior="Forever"/>

<ColorAnimation From="Red" To="Yellow" Duration="0:0:02" Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)" RepeatBehavior="Forever"></ColorAnimation>

</Storyboard>

</BeginStoryboard>

</EventTrigger>

</Ellipse.Triggers>

</Ellipse>

</Grid>

 

Como podemos ver para poder animar el gradiente hacemos referencia a la ruta completa de la propiedad a la cual deseamos animar y nos referimos en específico a un GradientStop mediante su índice en la colección. El resultado que obtenemos es el siguiente:

 

Podemos notar que el color de la circunferencia ha cambiado de color rojo especificado hacia el color amarillo así como también se ha desplazado el gradiente hacia el color azul.

Eso es todo amigos cya next post!

“Tracking Services en Windows Workflow Foundation”
 

Uno de los principales beneficios que ofrece el motor de ejecución de Windows Workflow Foundation es que posee la capacidad de proveer servicios que tienen como objetivo el simplificar las tareas comunes a los cuales se enfrenta un desarrollador de flujos de trabajo.

El primero de los servicios que cubriremos en nuestro estudio ese servicio denominado "Tracking Services" que puede ser utilizados para una gran cantidad de objetivos.

Podemos definir a Tracking Services como el servicio que proporciona automáticamente el rastreo y/o seguimiento de la ejecución de cualquier instancia de un flujo de trabajo. Esta característica es particularmente importante debido a que en los flujos de trabajo muchas veces nos interesa saber qué actividades fueron ejecutadas y  en general el camino que se siguió el motor de ejecución para completar determinado  flujo de trabajo.     

El panorama en el cual a nosotros podemos hacer uso de esta característica es realmente amplio: Por ejemplo: Cuando utilizamos a WorkFlow Foundation como un proveedor de servicios de orquestación en un panorama de Enterprise Service Bus dentro de una arquitectura orientada servicios  es sumamente importante conocer los  servicios que fueron invocados desde la orquestación y los usuarios que utilizaron el sistema, en este escenario podemos utilizar los Tracking  services para monitorear la actividad que se está generando el momento de ejecutar los flujos de trabajo involucrados en orquestación de los servicios, en otras palabras la utilización de estos servicios será efectivamente en los escenarios de auditoría de monitoreo de flujos de trabajo.

Para poder acceder a los servicios que ofrece Tracking services hacemos uso del espacio de nombres System.Workflow.Runtime.Tracking que contiene las clases que nos permitirán generar ya sea a nuestra propia solución de monitoreo y seguimiento  de los flujos de trabajo o bien podemos utilizar soluciones que ya vienen listas para usarse como el SQL tracking service.

Antes de continuar los adentraremos un poco al arquitectura de Tracking Services.

Básicamente existen tres componentes principales dentro del arquitectura de los Tracking services y estos son:

1. Tracking Profiles (perfiles de seguimiento)

Los perfiles de seguimiento representan la manera con la cuál es posible identificar los orígenes y las fuentes de los eventos que deseamos capturar dentro del monitoreo del flujo de trabajo. Estos perfiles son indispensables al momento de generar nuestra propia solución de seguimiento. Existen tres tipos de eventos o sucesos a los cuales nos podemos suscribir para monitorearlos

a) Workflow Events (Eventos del flujo de trabajo):

Estos eventos surgen a nivel de instancia y son equivalentes a los eventos a los cuales nos suscribimos al momento de invocar un flujo de trabajo. Como ejemplo de estos flujos tenemos los eventos:  Created, Terminated, Suspended etc.

b) Activity Events (eventos de las actividades)

Estos eventos son generados por las actividades que están siendo ejecutadas dentro de la instancia del flujo de trabajo.

c) User Events (Eventos de usuario):

Existen ocasiones en las cuales es necesario obtener información adicional sobre lo que está sucediendo en un determinado flujo de trabajo y que no necesariamente corresponde a la situación de la instancia del mismo flujo de trabajo o de alguna de sus actividades sino que es necesario generar un suceso personalizado para capturar determinado comportamiento del flujo de trabajo.

Para cada uno de los eventos descritos previamente existe una clase específica que guarda información sobre los mismos. Nos referimos a las clases WorkflowTrackingRecord ActivityTrackingRecord y UserTrackingRecord respectivamente

2. Tracking Runtime (Motor de ejecución de los servicios de seguimiento)

El motor de ejecución de los servicios de seguimiento de los flujos de trabajo se encarga de iniciarlo servicios de monitoreo que han sido declarados antes de la invocación de la instancia del flujo de trabajo que deseamos monitorear para hacer esto utiliza la información encontrada dentro de los perfiles de seguimiento.

3. Channels (Canales)

Los  canales de monitoreo se usan para enviar los registros asociados a la instancia del flujo de trabajo cuando se encuentra un tracking point que es la clase genérica que recibe información de lo que está sucediendo dentro del flujo de trabajo.

Crearemos a continuación la implementación básica de un servicio de seguimiento personalizado.

1)  Iniciaremos con una forma a la que llamaremos FormaInicio, en la cual colocaremos dos etiquetas, una de las cuales usaremos como el receptor de la información que generan las actividades del flujo de trabajo, y otra como los mensajes que genera el servicio de seguimiento (Tracking Service) que generaremos en este ejemplo.

 

2) Añadiremos a continuación a la solución un flujo de trabajo secuencial y agregaremos una actividad del tipo codeActivity

 

3) En el código de codeActivity1 simplemente escribiremos en la etiqueta un mensaje con el siguiente código

C# 

   1:  
   2:  
   3:         private void codeActivity1_ExecuteCode(object sender, EventArgs e)
   4:  
   5:         {
   6:  
   7:             Programa.FormaInicio.lblMensaje.Text += "MENSAJE DESDE LA CODEACTIVITY1\n";           
   8:  
   9:         }
  10:  
  11:             }

 

4) Posteriormente vamos a invocar la ejecución del flujo de trabajo en el manejador de evento del clic del botón de la forma y generaremos la suscripción al evento WorkFlowCompleted del flujo de trabajo con el código que aparece a continuación

C# 

private void button1_Click(object sender, EventArgs e)
 
       {
 
           WorkflowRuntime MotorWF = new WorkflowRuntime();
 
           Type Tipo = typeof(Workflow1);
 
           WorkflowInstance InstanciaWF = MotorWF.CreateWorkflow(Tipo);
 
           InstanciaWF.Start();
 
           MotorWF.WorkflowCompleted +=
 
               new EventHandler<WorkflowCompletedEventArgs>(MotorWF_WorkflowCompleted);
 
       }
 
       void MotorWF_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
 
       {
 
           MessageBox.Show("¡El flujo de trabajo fue finalizado!");
 
       }
 
 

Al momento de ejecutar el código previamente mostrado en la etiqueta que recibe la información generada desde flujo de trabajo deberá aparecer el mensaje "MENSAJE DESDE LA CODEACTIVITY1"

5) Hemos llegado al momento en el cuál propiamente iniciaremos el desarrollo del servicio de tracking. Añadiremos una nueva clase a nuestro proyecto que denominaremos ServicioSeguimiento. Esta clase deberá heredar de la clase TrackingService que es la clase abstracta base que provee la interfase entre un servicio de seguimiento y el motor de ejecución de los servicios de seguimiento que mencionamos anteriormente en los conceptos de arquitectura de este capítulo.

Para generar exitosamente una clase que pueda ser consumida por el motor de seguimiento es necesario sobreescribir (hacer override) los siguientes miembros de la clase base TrackingService.

  • GetProfile(Guid),
  • GetProfile(Type, Version)
  • TryGetProfile
  • GetTrackingChannel
  • TryReloadProfile.

El motor de ejecución de los servicios de seguimiento solicita un objeto del tipo TrackingChannel para cada una de las instancias que tienen un TrackingProfile asignado y usa este TrackingChannel para mandar los registros asociados a dar una instancia de flujo de trabajo.  Dentro de la definición del TrackingProfile se debe de especificar cuáles serán los eventos que serán monitoreados y cuya información es recibida por un TrackingRecord que como vimos anteriormente, puede tener como clases concretas los tipos:  ActivityTrackingRecord,  UserTrackingRecord o WorkflowTrackingRecord.

Posteriormente la infraestructura de WF manda a llamar el método TryReloadProfile para verificar si se debe recargar el Profile. Este proceso permite a un cliente o a un servicio cambiar un profile de seguimiento cambiar dinámicamente.

C#

public class ServicioSeguimiento : TrackingService
 
    {
 
        protected override bool TryGetProfile(Type workflowType, out TrackingProfile profile)
 
        {
 
            //El  objetivo de este método es que el motor de seguimiento  
 
            //dependiendo del tipo de WF el servicio puede utilizar diferentes tracking
 
            //profiles. Aunque es necesario hacer el override en este ejemplo utilizaremos
 
            //para que el ejemplo compile en esta muestra usaremos
 
            //el mismo profile para cualquier WF
 
            profile = ObtenerProfile();
 
          return true;
 
        }
 
        protected override TrackingProfile GetProfile(Guid workflowInstanceId)
 
        {
 
            // No será implementado para este ejemplo
 
            throw new NotImplementedException("No implementado");
 
        }
 
        protected override TrackingProfile GetProfile(Type workflowType, Version profileVersionId)
 
        {
 
          return ObtenerProfile();
 
        }
 
        protected override bool TryReloadProfile(Type workflowType, Guid workflowInstanceId, out TrackingProfile profile)
 
        {
 
            // En este caso siempre regresamos falso indicando que no hay nuevos profiles
 
            profile = null;
 
            return false;
 
        }
 
        protected override TrackingChannel GetTrackingChannel(TrackingParameters parametros)
 
        {
 
            //El motor de WF llama este método para obtener el canal para la instancia de monitoreo
 
            return new CanaldeSeguimiento(parametros);
 
        }
 
        private TrackingProfile ObtenerProfile()
 
        {
 
                // Creamos un  Tracking Profile
 
            TrackingProfile profile = new TrackingProfile();
 
            profile.Version = new Version("3.0.0");
 
            // En este caso monitorearemos específicamente actividades, no otro tipo de eventos
 
            ActivityTrackPoint PuntoSeguimiento = new ActivityTrackPoint();
 
            ActivityTrackingLocation LocacionActividad = new ActivityTrackingLocation(typeof(Activity));
 
            LocacionActividad.MatchDerivedTypes = true;
 
            //Con este código se registran todos los posibles estados
 
            IEnumerable<ActivityExecutionStatus> estados = Enum.GetValues(typeof(ActivityExecutionStatus)) as IEnumerable<ActivityExecutionStatus>;
 
            foreach (ActivityExecutionStatus estado in estados)
 
            {
 
                LocacionActividad.ExecutionStatusEvents.Add(estado);
 
            }
 
            PuntoSeguimiento.MatchingLocations.Add(LocacionActividad);
 
            profile.ActivityTrackPoints.Add(PuntoSeguimiento);
 
            return profile;
 
        }
 
         }

Como podemos ver en el código presentado anteriormente el método GetTrackingChannel devuelve un objeto del tipo TrackingChannel donde en este caso específicamente estaremos enviando a la forma la información que ha sido monitoreada automáticamente al momento de ser ejecutado el flujo de trabajo.

6) A continuación añadiremos a nuestro programa una nueva clase denominada CanaldeSeguimiento que heredará de TrackingChannel cuyo objetivo será manejar para persistirlos. En nuestro ejemplo simplemente se envía la información recopilada a la etiqueta que se encuentra en la forma.

De esta clase es necesario sobre escribir los métodos heredados desde la clase abstracta Send y InstanceCompletedOrTerminated para lograr un código como el siguiente:

public class CanaldeSeguimiento : TrackingChannel
 
    {
 
        private TrackingParameters Parametros = null;
 
        protected CanaldeSeguimiento()
 
        {
 
        }
 
        public CanaldeSeguimiento(TrackingParameters parametros)
 
        {
 
            this.Parametros = parametros;
 
        }
 
        //Este es el método que se ejecuta para realizar el registro de la actividad generada por el WF
 
        protected override void Send(TrackingRecord Registro)
 
        {
 
            ActivityTrackingRecord RegistroActividad = (ActivityTrackingRecord)Registro;
 
           Programa.FormaInicio.lblSeguimiento.Text+="Hora: " + RegistroActividad.EventDateTime.ToString();
 
           Programa.FormaInicio.lblSeguimiento.Text += "Fecha: " + RegistroActividad.QualifiedName.ToString();
 
           Programa.FormaInicio.lblSeguimiento.Text += "Tipo: " + RegistroActividad.ActivityType;
 
           Programa.FormaInicio.lblSeguimiento.Text += "Estado: " + RegistroActividad.ExecutionStatus.ToString();
 
        }
 
        //Se llama cuando se termina de ejecutar la instancia
 
        protected override void InstanceCompletedOrTerminated()
 
        {
 
            MessageBox.Show("Se terminó la instancia");
 
        }
 
    }
 

Hemos descrito entonces los pasos básicos para generar un servicio de seguimiento personalizado para cualquier instancia de un flujo de trabajo, que monitorea la ejecución de actividades y en este caso muestra la información a la etiqueta definida en nuestra forma principal oct.

Lo único que falta para que nuestro Tracking Service personalizado funcione es agregar el servicio al momento de que se invocan las islas de los flujos de trabajo es decir, en el  debemos de modificar nuestra invocación con la siguiente línea de código

private void button1_Click(object sender, EventArgs e)
 
       {
 
           WorkflowRuntime MotorWF = new WorkflowRuntime();
 
       //Añadimos el servicio que acabamos de crear
 
         MotorWF.AddService(new ServicioSeguimiento());
 
           Type Tipo = typeof(Workflow1);
 
           WorkflowInstance InstanciaWF = MotorWF.CreateWorkflow(Tipo);
 
           InstanciaWF.Start();
 
           MotorWF.WorkflowCompleted +=
 
               new EventHandler<WorkflowCompletedEventArgs>(MotorWF_WorkflowCompleted);
 
       }
 
       void MotorWF_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
 
       {
 
           MessageBox.Show("¡El flujo de trabajo fue finalizado!");
 
       }
 
 
 

Al momento de ejecutar el código anterior y oprimir el botón de invocación del flujo de trabajo podemos observar cómo el motor de ejecución de Workflow Foundation ejecuta los diferentes métodos definidos en las clases ServicioSeguimiento y CanaldeSeguimiento y muestra en la forma el resultado del seguimiento del flujo de trabajo

 

Como pudimos observar los ejemplo anterior podemos hacer un seguimiento muy específico de todas las situaciones que están ocurriendo durante la ejecución de nuestro flujo de trabajo para llevar a una auditoría muy a detalle de lo que sucede en el WF; sin embargo no siempre es necesario programar lo que hemos visto previamente sino que WF incluye una solución ya lista para usarse basada en SQL Server que se denomina SQLTracking y que hace lo mismo que vimos anteriormente pero su persistencia es evidentemente el servidor de base de datos SQL Server y nosotros lucharemos que programar absolutamente nada porque todo ya está hecho. Cabe señalar que ésta implementación utiliza la misma técnica y usa como clase de bases las mismas clases que utilizamos para nuestro tracking service personalizado.

Para poder utilizar esta funcionalidad es necesario tener instalada correctamente una instancia de SQL Server 2005 (Express o cualquier versión comercial) y ejecutar unos scripts que se distribuyen con la instalación del .NET Framework 3.0 y que se encuentran en C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\EN

Estos scripts son:

  • Tracking_Schema.sql Crea la estructura de SQLTracking en la BDD.
  • Logic_Schema.sql, Crea la lógica de negocio de SQLTracking en la BDD.

Modificando nuestro ejemplo previo agregaremos ahora el servicio SqlTrackingService que como podemos ver contiene un constructor sobrecargado en el cual le pasaremos como parámetro la cadena de conexión que indica la instancia y el nombre de la base de datos de SQL Server.

private void button1_Click(object sender, EventArgs e)

        {

            WorkflowRuntime MotorWF = new WorkflowRuntime();

            MotorWF.AddService(new ServicioSeguimiento());

            //Agregamos servicio SQLTrackingService;

            MotorWF.AddService(new

            SqlTrackingService(@"Data Source=.\sqlexpress;" +

            "Initial Catalog=Tracking;Integrated Security=sspi"));

            Type Tipo = typeof(Workflow1);

            WorkflowInstance InstanciaWF = MotorWF.CreateWorkflow(Tipo);

            InstanciaWF.Start();

            MotorWF.WorkflowCompleted +=

                new EventHandler<WorkflowCompletedEventArgs>(MotorWF_WorkflowCompleted);

        }

Podemos ahora ir al Management Studio de SQL Server y ejecutar el siguiente query que nos mostrará la efectividad del SqlTrackingService:

T-SQL

SELECT  TrackingWorkflowEvent.Description as Evento, 
 
WorkflowInstanceEvent.EventDateTime as HoraInicial, 
 
WorkflowInstance.WorkflowInstanceId as InstanciaWF, 
 
Type.TypeFullName as NombreWF
 
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

El query anterior nos mostrará el siguiente resultado

Persistencia

Existe otro concepto muy importante dentro de los flujos de trabajo y que también es provisto mediante los servicios de ejecución del motor de flujos de trabajo de Workflow Foundation es la persistencia. Este concepto se le denomina persistencia y es utilizada siempre que se requiere recordar el estado de una determinada instancia de un flujo de trabajo para posteriormente ocuparla en otro momento del tiempo. Este tipo de flujos de trabajo son muy utilizados en los flujos humanos es decir aquellos en los cuales intervienen diferentes miembros una organización y cada uno completa una parte de las actividades específicas dentro del flujo de trabajo pero no lo hacen todos al mismo tiempo sino que cada quien interviene con sus actividades del flujo en momentos discontinuos. El motor de flujos de trabajo sobre una solución extremadamente simple y fácil de utilizar para implementar este tipo de flujo de trabajo también denominados flujos de trabajo de ejecución larga (Long running workflows)

Los servicios de persistencia de Workflow Foundation se encargan de guardar el estado de cualquier instancia de los flujos de trabajo en lo hacen de una manera transparente automática al momento de que ocurre algún evento que haga que el flujo de trabajo entre en un estado de suspensión es decir toda la información referente al flujo se serializa en un medio persistente (de ahí el nombre)

Modificaremos ahora el proyecto previamente usado para verificar el funcionamiento de los servicios de persistencia de WF.

1) Lo primero que haremos será modificar nuestro flujo de trabajo agregando una actividad del tipo SuspendActivity y  otra CodeActivity después de la primera, cuyo código será: Programa.FormaInicio.lblMensaje.Text += "MENSAJE DESDE LA CODEACTIVITY2\n";

 

2) Modificaremos también la forma principal del proyecto (frmInicio) y le agregaremos una caja de texto, así como un botón adicional, de tal manera que quede parecida a la siguiente:

3) Modificaremos ahora la invocación del flujo de trabajo en el primer botón para añadir el servicio de SqlWorkflowPersistenceService que se encuentra dentro del espacio de nombres System.Workflow.Runtime.Hosting, así mismo agregaremos un manejador de eventos para que el host se suscriba al evento que generará la SuspendActivity y que notificará al host de dicha suspensión  tal modo que la inicialización del motor del flujo de trabajo deberá parecerse a la siguiente:

private void button1_Click(object sender, EventArgs e)
 
        {
 
            WorkflowRuntime MotorWF = new WorkflowRuntime();
 
            MotorWF.AddService(new ServicioSeguimiento());
 
            //Agregamos servicio SQLTrackingService;
 
            MotorWF.AddService(new 
 
            SqlTrackingService(@"Data Source=.\sqlexpress;" +
 
            "Initial Catalog=WF;Integrated Security=sspi"));
 
//Agregamos servicio de persistencia
 
            MotorWF.AddService(new 
 
SqlWorkflowPersistenceService (@"Data Source=.\sqlexpress;" +
 
            "Initial Catalog=WF;Integrated Security=sspi"));
 
            Type Tipo = typeof(Workflow1);
 
            WorkflowInstance InstanciaWF = MotorWF.CreateWorkflow(Tipo);
 
              MotorWF.WorkflowCompleted +=
 
                new EventHandler<WorkflowCompletedEventArgs>(MotorWF_WorkflowCompleted);
 
 
            //Nos suscribimos a la suspensión del flujo de trabajo por la SuspendActivity
 
// que añadimos al WF.
 
MotorWF.WorkflowSuspended +=
 
                new EventHandler<WorkflowSuspendedEventArgs>(MotorWF_WorkflowSuspended);
 
            InstanciaWF.Start();
 
        }

4) En el manejador de de eventos determinado por la función MotorWF_WorkflowSuspended agregaremos el siguiente código para obtener el id de instancia del WF que usaremos posteriormente para solicitar al motor de WF que reactive el flujo de trabajo persistido. Haremos esto mediante el siguiente código.

C#

void MotorWF_WorkflowSuspended(object sender, WorkflowSuspendedEventArgs e)
 
       {
 
           e.WorkflowInstance.Unload();
 
           MessageBox.Show("WF Persistido!!!");
 
           //Agregamos el id de instancia a caja de texto para hacer la reactivacion
 
           this.textBox1.Text = e.WorkflowInstance.InstanceId.ToString();
 
       }
 

5)Para finalizar, al nuevo botón que acabamos de agregar le añadiremos el siguiente código que invoca a los servicios de persistencia para reactivar el flujo de trabajo previamente persistido (debido a que fue suspendido y descargado en el paso previo) utilizando como su identificador el id de la instancia que se encuentra en la caja de texto.

C#

 

private void button2_Click(object sender, EventArgs e)
 
       {
 
           WorkflowRuntime MotorWF = new WorkflowRuntime();
 
           MotorWF.AddService(new
 
            SqlWorkflowPersistenceService(@"Data Source=.\sqlexpress;" +
 
          "Initial Catalog=WF;Integrated Security=sspi"));
 
           MotorWF.StartRuntime();
 
           MotorWF.WorkflowCompleted +=
 
              new EventHandler<WorkflowCompletedEventArgs>(MotorWF_WorkflowCompleted);
 
            WorkflowInstance InstanciaWF = MotorWF.GetWorkflow(new Guid(this.textBox1.Text));
 
            InstanciaWF.Resume();
 
       }
 

6) Al ejecutar el flujo de trabajo podemos observar que la instancia es suspendida después de que se ejecuta la primera actividad de código, y posteriormente la caja de texto adquiere el GUID que representa la instancia del WF.

 

7) Al oprimir el botón con el texto Reactivar WF se reactivará el flujo de trabajo persistido mediante SQLPersistenceServices y el motor de ejecución de WF completará el proceso  de ejecución del flujo de trabajo.

Bueno pues ojalá les haya latido el artículo. El código fuente completo de este artículo está aqui

Maic.