Buscar en este blog....

Cargando...

lunes, 14 de marzo de 2016

Mi experiencia en el Agile Open Camp 2016

Hace una semana comenzaba a terminar el AOC 2016, la segunda edición del Agile Open Camp que se celebra en Argentina desde el año pasado. Fui afortunado al poder participar. Quiero contar un poco lo que es o, más bien, lo que fue para mi.

Pero es difícil comenzar. Es dificil describir con palabras algo que te emociona, que toca tus sentidos mucho más allá del simple aprendizaje de la filosofía Agile. Porque no se ven exactamente Metodologías Ágiles; para eso ya está plagado internet de libros y artículos. Porque no te explican cómo implementar tal o cual cosa; para eso hay manuales. Vas a compartir.

Keynote de Juan Daza, a orillas del río.

En estos Open Camps, vivís tres días y medio aislado del mundo, solo en contacto con otras decenas de personas que buscan lo mismo que vos: compartir momentos y experiencias. El tronco temático es Agile, sí, pero vos podés compartir o escuchar lo que vos quieras. Vos podés aprender de Arduino, de  microservicios, de scrum, de bitcoins, nociones sobre el movimiento Slow, e incluso podés pedir que si alguien sabe, te enseñe a tocar guitarra. Hay también quienes se ofrecen para darte un taller de astronomía o eseñarte fotografía nocturna. Pero vos podés ofrecer o pedir lo que quieras.

Pero por supuesto hay mucho de Agile, experiencias, charlas, workshops, y una modalidad que me encantó que se llama "World Cafe", en la que se discuten temas en mini sesiones de 20 minutos. Y se vive en carne propia la magia de la auto-organización del evento. Vivir la experiencia de esta auto-organización no te lo da ningún libro, ningún curso, ninguna conferencia, ningún congreso. Acá cada uno es libre de participar en lo que quiera (y si es que quiere).

En medio del evento, de pronto podés conocer a alguien con quién te das cuenta que podés combinarte y dar un taller, gestar una idea que, quien te dice, germine con el tiempo para salir a la luz en forma de un emprendimiento, un proyecto, o simplemente conocer un buen amigo.

Auto-organizando talleres.

No menor es el hecho de que el evento se realiza (al menos hasta ahora) en diferentes lugares cercanos -pero no dentro- de la ciudad de San Carlos de Bariloche (Argentina), cuyos paisajes y tranquilidad, condimentan con el sabor mágico que hace falta para crear ese ambiente relajado e inspirador que lo caracteriza y lo hace tan especial, porque convengamos que no sería lo mismo hacer el este evento en el medio de la ciudad, con los ruidos y distracciones que abudan allí. Acá estás "obligado" a compartir con la misma gente todo el tiempo, y eso tiene un efecto impresionante.

En esta edición en particular, tuvimos la oportunidad de asistir a muchísimos talleres, jugar al fútbol, al paddle, hacer caminatas por la zona en medio de árboles y bordeando ríos, meditar, y hacer un fogón en medio de lo que fue la noche más silenciosa y estrellada de mi vida; con guitarras, chistes, charlas y un corderito que rebasaba absolutamente cualquier tipo de pretención que pudierámos tener.

Cronograma tentativo.
Lo importante, dicen algunos, no es sólo pasarla bien durante los días que dura el evento. Lo importante es que, además, el evento se detenga mágicamente en el tiempo dentro de cada uno de nosotros, adoptando la curiosa forma de la motivación y, de alguna manera, pase a formar parte los días subsiguientes, y los meses, y si es posible de la vida. Es decir, que produzca un cambio en nosotros, que nos llene de energía para poder comenzar una transformación como personas, mejorar las relaciones con los demás, generar confianza, y crecer.

Sigo sintiendo que no puedo aún expresar la magia que me dejó esta experiencia. Creo que si tengo que compararlo con algo para explicárselo a quien nunca fue, es como un retiro espiritual de agilidad. No soy creyente, pero es una comparación muy acertada, porque te quedás con esa hermosa sensación de "yo puedo -y voy- a cambiar el mundo".

Para terminar, quiero citar a Mauro Strione, uno de los organizadores principales del AOC, que expresa en lindas palabras, lo que muchos sentimos una vez que terminó el evento:

"Fueron tres días llenos de energía, motivación, compromiso, ganas... y sin obligar a nadie a nada, es más, todos vienen porque quieren, no porque los mandan de su empresa. La mayoría se lo pagan de su bolsillo..."

"No se si logro transmitir de qué se trata esto, quizás suene a secta o a que estamos locos. Solamente quiero advertir que cuando los geeks descubrimos el potencial de los ceros y unos de las computadoras, transformamos el mundo en pocos años, si ahora empezamos a entender a las personas y sus relaciones, sus sentimientos y motivaciones, y logramos combinarlos con esos ceros y unos... cuidado con lo que podemos lograr!"


Fotografía de las noches de Bariloche.

miércoles, 9 de marzo de 2016

C# - Obtener canales RGB de un Bitmap

Vamos a trabajar con el siguiente bitmap tomado de internet para trabajar:

 

Para descomponer un Bitmap de tres canales (RGB) en tres Bitmaps que representen cada uno, un canal del Bitmap original, podemos hacer lo siguiente:


rgb = new Bitmap("imagen.bmp");
int width = rgb.Width;
int height = rgb.Height;

Bitmap channelRed = new Bitmap(width, height);
Bitmap channelGreen = new Bitmap(width, height);
Bitmap channelBlue = new Bitmap(width, height);

for(int x = 0; x < width; x++)
{
   for(int y = 0; y < height; y++)
   {
      Color color = rgb.GetPixel(x, y);
      Color colorRed = Color.FromArgb(color.R, color.R, color.R);
      Color colorGreen = Color.FromArgb(color.G, color.G, color.G);
      Color colorBlue = Color.FromArgb(color.B, color.B, color.B);

      channelRed.SetPixel(x, y, colorRed);
      channelGreen.SetPixel(x, y, colorGreen);
      channelBlue.SetPixel(x, y, colorBlue);
   }
}

De esta forma, obtenemos 3 bitmaps, cada uno en blanco y negro y representando la tonalidad de cada canal:

 

Esto se ve así, porque ocupamos cada canal, de cada imagen, con el canal que obtuvimos previamente. Es decir, extrajimos el canal rojo de la imagen original, y grabamos todos los canales de channelRed con ese componente. Al tener cada canal el mismo valor, se ve gris. Si quisieramos que cada imagen contenga únicamente el valor del canal que extraemos, tendríamos que dejar los otros dos canales en cero. Cambiemos entonces la parte del código que asigna los colores:

Color color = rgb.GetPixel(x, y);
Color colorRed = Color.FromArgb(color.R, 0, 0);
Color colorGreen = Color.FromArgb(0, color.G, 0);
Color colorBlue = Color.FromArgb(0, 0, color.B);

Y lo que obtenemos es esto:


Si bien el resultado es el esperado, esta forma no es precisamente rápida. En caso de imágenes más grandes, el proceso tarda más, y más. Y ni hablar si queremos operar con estos valores. Hay, claro, formas de trabajar con esto mucho más rápido, en otro momento las veremos.

lunes, 15 de febrero de 2016

¿Qué es el NDVI, o Indice de Diferencia Normalizada de Vegetación?

Esta vez no voy a hablar de ningún concepto del software, y me permito divagar por otros ámbitos que no se le parecen demasiado, como es el tema del agro. No se parecen, pero están relacionados. ¿Por qué? Porque TODO, tarde o temprano, termina esclavizando al desarrollo de software para sobrevivir. Los informáticos, somos una espcie necesaria para la industria, nos guste o no.

Hace unos años, allá a madiados de de los setenta -si mal no recuerdo-, un científico se hallaba en su trabajo, ni más ni menos que la NASA, analizando imagenes satelitales del LandSAT (un satelite que tomaba fotografías areas de la Tierra). Su estudio se orientaba al análisis de la vegetación sobre la tierra.

Este señor, cuyo nombre era Compton Tucker, después de mucho analizar, y obtener resultados, escribió un paper llamado "Red and Photograghic Infrared Linear Combinations for Monitoring Vegetation", es decir: Combinaciones Lineales del Rojo e Infrarojo Fotográfico para Monitoreo de la Vegetación". De los resultados que obtuvo, nos vamos a ocupar en este post.

Aunque a simple vista puede sonar aburrido a quienes no son del palo de la agricultura -y similares-, lo que este señor encontró es sorprendente: Un árbol en buen estado de salud refleja muchísima luz infraroja, y muy poca luz roja. Pero si este árbol se enferma, entonces comienza a reflejar muchísima menos luz infraroja, casi en la misma cantidad que la luz roja. Y si se muere dicho árbol, entonces deja de irradiar luz infraroja, comparado con la luz roja. Todo esto tiene una explicación lógica, que está relacionada con la clorofila, pero por ahora no nos interesa.

Me permito tomar prestada una imagen de internet para ilustrar este concepto con imágenes, que siempre resulta más comprensible y entretenido:


Aunque está en inglés, es bastante simple:
  • La hoja saludable (Healthy Leaf) refleja muchísismo la luz infraroja (NIR), y bastante luz verde y es por eso que un arbol sano se ve de color verde.
  • La hoja estresada (Stressed Leaf) refleja muchísimo menos infrarojo, y un poco más de rojo y verde.
  • La hoja muerta (Dead Leaf) refleja aún menos infrarojo que la estresada, y menos verde, por eso se ve más opaca y apagada.
A esta altura, tiene que estar clarísimo que nosotros los seres humanos NO podemos ver los rayos infrarojos, y por eso no nos damos cuenta si un arbol refleja más o menos luz infraroja. No señor, no podemos. Pero en nuestras casa, y a diario, contamos con una herramenta que SÍ distingue la luz infraroja: las cámaras de fotos digitales. Si alguna vez te preguntaste por qué yo no puedo ver la luz que emite un control remoto, pero cuando lo miro a través de una cámara de fotos sí puedo verla, ésta es la razón. El sensor de la cámara distingue la luz infraroja y la convierte en luz visible que puede variar entre blanco, violeta, o rosado. (TIP: esto se usa mucho para comprobar si un control remoto funciona, o no).

Volviendo al señor Compton, lo que hizo de interesante, fue describir algunos modelos que se basaban en relaciones matemáticas entre la luz roja y la luz infraroja, y que permiten comprobar el estado de salud de una vegetación.

De todas las que hizo, la fórmula que más le convenció, se llama NDVI (Normalized Difference Vegetation Index), o Índice de Diferencia Normalizada de Vegetación, y es la siguiente:


 dónde NIR representa la luz infrarroja, y Red -obviamente- la roja.

Esta fórmula nos da valores que se encuentran entre -1 y 1. Mientras más cercano al -1, peor es la salud de la planta, y mientras más cercano a 1, mejor es la salud de la planta. Los valores intermedios, corresponden a estados de estrés, aunque para comprender realmente el significado o el motivo de los números, es siempre conveniente consultar con un experto en el tema del agro.

Mediante la aplicación correcta de esta fórmula, que Compton utilizo con imagnes satelitales, podemos obtener nuevas imágenes que nos muestran el estado de la vegetación que hablamos recién. Aquí tenemos un ejemplo, pero con imagenes no-satelitales:

Primero, contamos con esta imagen normal tomada por una cámara común y corriente, y que puede también guardar los infrarojos:


Luego de aplicarle la fórmula del NDVI, obtenemos la siguiente imagen:


Donde podemos distinguir claramente qué parte de la vegetación está más saludable (la verde) y cual más seca (naranja). Y además, podemos distinguir perfectamente aquellas partes que no son vegetación (rojo).

Es sorprendente la claridad de la información que nos da la imagen! Esto se utiliza mucho con fotos aereas de cultivos, para encontrar problemas en el riego, o de otra indole que pueden estar afectando dichos cultivos. Es una forma relativamente económica y fiable, de la cual también se pueden llegar a encontrar patrones de problemas.

En este momento estoy desarrollando un software para que, dada una imágen, calcule la imágen NDVI correspondiente. De hecho, de estas imágenes que uso como ejemplo, la del NDVI fue calculada con mi software. Si bien el software está funcionando, aún debo pulir la interfaz de usuario, para hacerlo más amigable.

Esto es todo por ahora, más adelante presentaré este programa que permite automatizar el cálculo de NDVI, y en tiempos muy cortos.

Saludos!

viernes, 21 de agosto de 2015

Diferencia -y relación- entre los patrones MVC y MVP

Creo que este tema se presta un poco a confusión debido a la cantidad de información que existe en internet y el poco tiempo que disponemos para analizar toda esta información.

El patrón MVC (Modelo-Vista-Controlador) es muy utilizado y creo muy pocas personas del ambiente del desarrollo no lo conocen, por lo que no voy a hablar de este patrón.

Sin embargo, no tan famoso es éste otro patrón, que ha tomado un poco más de fuerza desde hace unos años, y sigue escalando: el patrón MVP. El patrón MVP hace referencia a: MODELO-VISTA-PRESENTADOR.

A simple vista, uno se siente tentado a pensar: "Ah! Cambiaron el Controlador del MVC por algo nuevo llamado Presentador, que debe hacer algo parecido pero con otro nombre.."

Ja! Nada más alejado de la realidad! Absolutamente no! Entonces, ¿qué es?

Si bien no voy explicar qué es, porque sería seguir agregando más información de lo mismo, y en google ya hay mucha y buena, yo quiero hablar sobre qué relación existe entre ambos patrones, y por ende, también hablar de cuál es la diferencia.

Primero que nada, recordemos rápidamente que la intención del MVC es separar una aplicación en tres partes fundamenales, pero de alguna manera relacionadas. Tenemos:

  •  Modelo: que hace referencia a los datos o, mejor dicho, a la representación de la información. Según cómo se interprete el patrón, el modelo puede estar directamente ligado a la persistencia de dicha información. Sin mebargo, en una interpretación más amplia, puede abarcar también el estado del negocio en un momento dado. Es decir que, en su versión más amplia, el modelo es el estado del negocio y la persistenca de ese estado.
  • Controlador: que representa la lógica del negocio. Las reglas que hacen al negocio.
  • Vista: que representa la interfaz con el usuario.

Acá quiero aclarar que yo particularmente no estoy muy de acuerdo con la separación entre "Modelo" y "Controlador", porque pienso que muchas veces es dificil y costoso excluir totalmente al modelo (representación de los datos) de la lógica del negocio (o sea, del controlador). Sin embargo, no estoy diciendo que no se pueda hacer, o no se deba hacer, sino más bien que hay que considerar que existe una separación coneptual de ambas cosas, sin que esto implique necesariamente llevarla a cabo (aspecto que será inherente a cada situación).


Ahoa bien, si repasamos brevemente el MVP, nos encontramos con lo siguiente:


  • Modelo: aquí el modelo representa el modelo del negocio. Voilá! No es lo mismo que el modelo del MVC, sino que este modelo incluye tanto la representación de los datos como la lógica del negocio: por eso hablamos de "modelo del negocio".  Es decir que este modelo abarca al Modelo y al Controlador del MVC juntos.
  • Presentador: El presentador es quien dirige: por un lado los eventos de la vista hacia el modelo, y por otro lado actualiza la vista con las información provenientes del modelo. Este concepto no existe directamente dentro del patrón MVC. Digo "directamente" porque es perfectamente válido afirmar que este presentador es parte de la Vista del MVC. Es decir, es la lógica que le solemos poner a la vista, por más básica que sea!
  • Vista: La vista, según el MVP, es lo más tonto y fácil del programar que hay. Se trata de una vita propiamente dicha que conoce a su presentador, a quién le delega absolutamente todo. Ella no hace nada, excepto delegar. Ocurre un click: le dice al presentador que se haga cargo del click. Asi de simple. Ocurre algún otro evento: le dice al presentador que se encargue de ese evento. Pero ella no procesa nada, no hace nada. Solo delega el trabajo al presentador.

Despues de analizar un poco esto, podemos llegar a la conclusión de que ambos patrones no son excluyentes. No es cuestión de decir Vamos a usar el MVC o el MVP, sino más bien, puede ser interesante pensar en una combinación de ambos. Algo así como un VPCM (Vista-Presentador-Controlador-Modelo).

Qué!? Eso no existe! De qué estamos hablando?

De nada nuevo, en realidad. Sólo estamos desglosando la Vista del MVC en dos partes: una vista que no hace nada, y su presentador que hace todo. Es decir, de la Vista del MVC obtenemos la Vista y el Presentador del MVP. Algo así:


 
Otra forma de verlo, es la inversa: agarramos el Modelo del MVP y los desglosamos en un controlador (con la lógica del negocio) y un modelo de datos (represetación de los datos). Es decir, del Modelo del MVP obtenemos el Controlador y el Modelo del MVC. Algo así:



Cómo podemos ver después de este análisis, NO son patrones mutuamente excluyentes: son complementarios. Por decirlo así, uno hace más hincapié en el back-end, y el otro en el front-end.
Me gustaría leer opiniones que aporten a este análisis y ésta conclusión.
 Saludos!

viernes, 31 de julio de 2015

Aspectos en C# con Postsharp (Un ejemplo muy simple)

Buenas!
Como algunos recordarán, hace unos años comencé a incursionar en el mundo de la Programación Orientada a Aspectos. En aquella época, hice tres entradas explicando qué es la programación orientada a aspectos, qué ventajas tiene, y hasta di un ejemplo de cómo implementarla en Java usando AspectJ. De eso hae ya unos años. Sin embargo, dichas entradas siguen más vigentes que nunca!! (Así que si no sabés muy bien lo que son y querés una introducción corta y clara, te recomiendo las dos primeras antes de seguir leyendo)

Sin embargo, la vida y los gustos me fueron llevando al mundo de .NET y C#, por lo que comencé a buscar nuevas herramientas. Hoy en día, hay realmente muchas herramientas y framworks para trabajar con aspectos. Sin embargo, yo preferí inclinarme al framework Postsharp.

Hasta ahora, admito me parece una maravilla la simplicidad de este framework comparado con otros, como Spring.NET AOP, que requieren un poquito más de maña con la configuración. Postsharp no necesita configurarse!!

Bueno, vamos al grano, y veamos un ejemplo de como funciona este sencillo framework.

Quizás lo correcto sería comenzar con algunas definiciones que no aclaré en los posts pasados, y que son de utilidad conceptual. Pero no: hoy comenzamos con un ejemplo, directamente. Después, en otro post aclararemos esos puntos.

Muy bien: vamos a suponer un escenario muy simple. Se trata de una compleja aplicación conocida como "Hola Mundo", a la cual le vamos a añadir un aspecto. Vamos a crear un solo aspecto que se encargue de interceptar la ejecución justo antes de ejecutar el método que escribe "Hola Mundo".

Paso 1: Agregar Postsharp al proyecto.

Una vez creado un proyecto vacío (por ejemplo, una consola), le agregamos el paquete de Postsharp. La manea más simple de hacerlo es, naturalmente, con NuGet. Tan simple como abrir NuGet y buscar el framework por su nombre. Luego agregarlo al proyecto.
Aquí vale aclarar, que una vez agregado al proyecto, Postsharp pide al usuario de descargar un ejecutable e instalarlo. (version VS > 2010). Esto es para instalar un componente agregado al Visual Studio. Postsharp tiene una versión gratuita que es suficiente para escribir aspectos con total libertad, pero también posee dos opciones más (que son pagas, y muy caras) las cuales proveen aspectos ya escritos y funcionando. Personalmente no he tenido el agrado de probar estas versiones aún. Pero sin miedo: instalá la versión Express (gratuita) para poder seguir. Esta instalación solo se realiza una vez.

Paso 2: Crear el Hola Mundo.

En realidad, los pasos 1 y 2 son intercambiables. Realmente es lo mismo. Podemos hacer primero el proyecto "base" (sin aspectos) y luego instalar Postsharp.

Para crear el proyecto "Hola Mundo", nada tan simple como en la clase principal hacer una llamada a la consola para escribir en pantalla

using System;

namespace PruebaPostSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hola Mundo!");
        }
    }
}


Este paso fue  fácil, no? ;) Sigamos!

Paso 3: Crear el Aspecto.

El aspecto es, nada más y nada menos, que una clase. Si así de simple. Bueno, es una clase con dos particularidades:

  1. SIEMPRE debe ser serializable. Esto se logra aplicando el atributo "Serializable" como ya veremos.
  2. SIEMPRE hereda de alguna clase padre proveniente de Postsharp, la cual le da los "poderes" de aspecto, por decirlo de alguna manera. En realidad, no son poderes, sino son clases que le permiten redefinir métodos (override) que ya tienen y que luego el core de Postsharp va ubicar en nuestro código. (Para nuestro ejemplo, lo ubicaría justo antes del llamado a "Main()".

Creemos una nueva clase, que se llame AspectoQueInterceptaUnMetodo y dice así:

using PostSharp.Aspects;
using System;

namespace PruebaPostSharp
{
    [Serializable]
    class AspectoQueInterceptaUnMetodo : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            Console.WriteLine("Entrado al método: " + args.Method.Name);
        }
    }
}

Brevemente quiero dar por sentado que ya sabemos donde esta indicado que la clase es serializable, y que el método hereda de la clase OnMethodBoundaryAspect, la cual le provee la posibilidad de reescribir algunos métodos. En este caso, elegimos el método OnEntry() que indica que lo que haga ese método se hará justo antes de ejecutar los métodos a los que se les aplica ese aspecto. Pero también hay otros: OnExit(), OnSuccess() y OnException(). Veamoslo en acción.

Otro detalle, es que en args.Method.Name tenemos almacenado el nombre del método que fue interceptado antes de llamar al aspecto. Esto, y mucho más (como acceso a los parametros, y los valores de los parámetros) son poderosas herramientas con las que podemos contar.
 

4) Indicar qué metodos serán afectados por el aspecto.

Hasta aquí tenemos un aspecto hecho y derecho. Pero está aislado: es decir, nadie lo usa. Para usarlo, solo debemos definirlo como atributo de el/los métodos a los que queremos que afecte.

De la siguiente manera:

 [AspectoQueInterceptaUnMetodo]
        static void Main(string[] args)
        {


Si ahora ejecutamos el proyecto, vemos que se muestra primero el texto del aspecto, y luego el del método Main. Esto es porque el aspecto siempre se ejecuta antes que el método. Si quisieramos que se ejecute después, deberíamos sobre escribir el método OnExit(). Si queremos que se ejecute antes y después, deberíamos sobreescribir ambos métodos: OnEntry() y OnExit(), y asi de simple funciona esto.

Otro tip interesante, es que si queremos que el aspecto se aplique a TODOS los métodos de una clase, solo basta con colocar el atriuto sobre la clase en cuestión:

    [AspectoQueInterceptaUnMetodo]
    class Program
    {

        static void Main(string[] args)
        {


Y de esta forma, cualquier método automáticamente será víctima de nuestro aspecto!! Simple, no?

Te invito a que pruebes vos mismo!

Esto fue una sencilla introducción a un aspecto muy simple. El mundo de aspectos es mucho más grande e interesante! Se pueden hacer cosas realmente estupendas pero, sin embargo, como todo en el software, siempre hay que analizar las ventajas y soluciones reales que esta (o cualquier otra) práctica puede conllevar. No siempre se necesitan aspectos.

En mi experiencia, el uso de aspectos es una tendencia que está creciendo, porque mucha gente está animándose a desarrollar con aspectos cada vez más gracias a los frameworks como postsharp, Spring.NET, etc Y si bien aún no es tan común, y aún no se lo práctica de la mejor forma, esto está cambiando de forma veloz, como todo en este campo.

Próximamente iremos mostrando cosas nuevas, más interesantes y más avanzadas!

Saludos!

viernes, 5 de junio de 2015

Web Service que recibe y devuelve JSON

La verdad es que si JSON se utiliza tanto hoy en día, decididamente su simplicidad de uso es uno de los factores más decisivos para esta tendencia.

Actualmente estoy haciendo unos Web Services en C# que deben ser consultados por un cliente enviando como request una cadena en formato JSON. Y no solo eso, el cliente también espera que el WS le devuelva su respuesta.. también en formato JSON.

Esto es, tremendamente simple de lograr. Para mostrar, voy a crear un proyecto muy simple. No será un WS porque no amerita la complicación. Quiero decir que lo que voy a hacer aquí se aplica a un WS o a un proyecto cualquiera... de hecho, voy a hacerlo para un método aislado.

Vamos a hacer una calculadora. Pero para darle un poco de "complejidad", vamos a hacer la calculadora reciba una lista de pares de números. Se tomarán los números de cada uno de estos pares y se los sumará. De forma que se devolverá una lista de "sumas". Por ejemplo:

Una lista para enviarle a la calculadora podría ser:
listaEntrada = [5,2],[1,-3],[-2,-2]
y la correspondiente lista de respuesta:
listaRepsuesta = 7,-2,-4

¿Estamos de acuerdo? Bien. Sigamos.

Si el Web Service va a recibir la lista de pares en formato JSON, debemos definir exactamente cómo.Podemos imaginar algo así:

{
    "pares": 
    [
  {
   "numero1":"5",
   "numero2":"2"
  },
  {
   "numero1":"1",
   "numero2":"-3"
  },
  {
   "numero1":"-2",
   "numero2":"-2"
  } 
    ]
}


y por lo tanto, la siguiente respuesta:

{  
   "sumas":[  
      7,
      -2,
      -4
   ]
}


Entonces, considerando esta decisión, vamos a armar dos entidades: una entidad para tener los datos de entrada, y otra entidad para poner los datos de salida. Las mismas podrían llamarse sumaRequest y sumaResponse, por ejemplo.

Escribamos estas entidades entonces, pero además agregamos una nueva: "Pares". Esta entidad, es el "tipo de dato" (por llamarlo de alguna manera) que contiene dos enteros, los cuales vamos a sumar.

public class sumaRequest
{
 public List<pares> pares { get; set; }
}


public class Pares
{
 public int numero1  { get; set; }
 public int numero2  { get; set; }
}


public class sumaResponse
{
 public List<int> sumas { get; set; }
}


Bueno, creando objetos de estas entidades y llenándolos con la información necesaria, ya podríamos trabajar perfectamente. LA operación suma, recibe la lista, y simplemente la procesa. Es decir, esto:

sumaResponse hacerTodasLasSumas(sumaRequest request)
{
 sumaResponse ret = new sumaResponse();
    ret.sumas = new List<int>();

 foreach(Pares pareja in request.pares)
 {
  int suma = pareja.numero1 + pareja.numero2;
  ret.sumas.Add(suma);
 }

 return ret;
}

Bueno, esto es muy lindo, pero aún no hemos metido a JSON en todo esto. Pero en realidad, es muy poco lo de tenemos que hacer. Solo hay que desserializar sumaRequest de un string que nos llegue, por otra parte serializar sumaResponse antes de devolverlo al usuario/cliente.

Para esto vamos a agregar el paquete JSON.NET desde Nuget (o la línea de comandos, como quieran). Una vez agregado, modificamos las entidades que directa o indirectamente entran en el JSON.
La diferencia entre entrar directamente o indirectamente, es simple: sumaRequest entra directamente, porque es la entidad que contiene la información del JSON de forma directa. Lo mismo ocurre con sumaResponse, que contiene la información directa que queremos almacenar. Sin embargo la entidad Pares entra de manera indirecta, ya que en realidad, debemos utilizar su contenido dentro del JSON, pero sólo por ser parte de la entidad sumaRequest.
Las entidades, las modificamos de la siguiente manera:

[JsonObject(MemberSerialization.OptIn)]
public class sumaRequest
{
 [JsonProperty(PropertyName = "pares")]
 public List<Pares> pares { get; set; }
}


[JsonObject(MemberSerialization.OptIn)]
public class Pares
{
 [JsonProperty(PropertyName = "numero1")]
 public int numero1  { get; set; }

 [JsonProperty(PropertyName = "numero2")]
 public int numero2  { get; set; }
}


[JsonObject(MemberSerialization.OptIn)]
public class sumaResponse
{
 [JsonProperty(PropertyName = "sumas")]
 public List<int> sumas { get; set; }
}


Lo que hemos hecho con el atributo JsonObject, es indicar que una clase forma parte de una Serializacion/Deserialización JSON (sin importar si es de forma directa o indirecta). Y con el atributo JsonProperty indicamos que el campo en cuestión debe ser serializado. Este atributo además permite especificar cómo se llama el campo cuando la información ser serializa/deserializa a JSON. Claramente, ambos nombres no tienen porqué coincidir.

Finalmente, y para terminar, debemos indicar en algun punto de código, que una string recibida debe deserializarse como entradaRequest, y que sumaResponse debe serializarse como salida. Esto lo hacemos con las funciones JsonConvert.DeserializeObject e JsonConvert.SerializeObject respectivamente. Nos queda así:

public string operacionDelWebService (string jsonRequest)
{
 sumaRequest objectRequest = JsonConvert.DeserializeObject<sumaRequest>(jsonRequest); // (1)

 sumaResponse objectResponse = hacerTodasLasSumas(objectRequest);    // (2)

 string response = JsonConvert.SerializeObject(objectResponse); // (3)

 return response;
}


Como vemos, la funcion -que podría ser la operación visible del WS- recibe y devuelve una cadena, ambas en formato JSON.

En (1) convertimos la cadena en formato JSON a objetos conocidos por nosotros (Deserializamos).
En (2) operamos normalmente con los objetos que conocemos.
En (3) convertimos la respuesta a un string con formato JSON. (Serializamos).

Luego se devuelve el string y todos contentos.
Espero que haya sido de utilidad y simple de entender.

Dejo el código completo funcional.


using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace Borrar02
{
    class Program
    {
        static void Main(string[] args)
        {

            string stringEntrada = " {\"pares\":[{\"numero1\":\"5\",\"numero2\":\"2\"},{\"numero1\":\"1\",\"numero2\":\"-3\"},{\"numero1\":\"-2\",\"numero2\":\"-2\"}]}";
            string stringSalida;

            WebService ws = new WebService();

            stringSalida = ws.operacionDelWebService(stringEntrada);
            
            Console.WriteLine("Respuesta del WS:\n{0}", stringSalida);
        }
    }

    class WebService
    {
        public string operacionDelWebService(string jsonRequest)
        {
            sumaRequest objectRequest = JsonConvert.DeserializeObject<sumaRequest>(jsonRequest);

            sumaResponse objectResponse = hacerTodasLasSumas(objectRequest);    // (2)

            string response = JsonConvert.SerializeObject(objectResponse); // (3)

            return response;
        }
        sumaResponse hacerTodasLasSumas(sumaRequest request)
        {
            sumaResponse ret = new sumaResponse();
            ret.sumas = new List<int>();

            foreach (Pares pareja in request.pares)
            {
                int suma = pareja.numero1 + pareja.numero2;
                ret.sumas.Add(suma);
            }

            return ret;
        }

        [JsonObject(MemberSerialization.OptIn)]
        public class sumaRequest
        {
            [JsonProperty(PropertyName = "pares")]
            public List<Pares> pares { get; set; }
        }

        [JsonObject(MemberSerialization.OptIn)]
        public class Pares
        {
            [JsonProperty(PropertyName = "numero1")]
            public int numero1 { get; set; }

            [JsonProperty(PropertyName = "numero2")]
            public int numero2 { get; set; }
        }

        [JsonObject(MemberSerialization.OptIn)]
        public class sumaResponse
        {
            [JsonProperty(PropertyName = "sumas")]
            public List<int> sumas { get; set; }
        }
    }
}

miércoles, 27 de mayo de 2015

Registrar un Plug-in en Microsoft Dynamics CRM 2015

Ahora si, llegó el momento prometido. Hemos hecho plugins, hemos llamado a webservices desde los plugins... pero ¡no hemos dicho como registrarlos para poder usarlos! Bueno, ahora mostramos cómo (es muy fácil!)

Igual, quiero aclarar que no pretendo dar información exhaustiva, sino solo la necesaria para probar los plugins que escribimos, y por lo tanto las indicaciones que voy a dar son muy básicas. Para más detalles y profundizar en el registro de plugins en el CRM, favor de remitirse a documentación más precisa.

Lo primero es descargar el SDK del Dynamics, esto se consigue acá. Después de descomprimirlo, vemos que hay una carpeta /tools y allí dentro, una carpeta llamada /PluginRegistration. Allí es dónde está la herramienta que se usa para registrar los plugins.

La abrimos, y vemos que necesitamos conectarnos al Dynamics. Creamos la conexión y entramos. Si, así de simple. Una vez dentro, vemos una pantalla igual -o similar- a ésta:



Daremos click a "Register" y luego "Register New Assembly":




Luego nos aparece una ventana para elegir el ensamblado que queremos agregar. Obviamente, el ensamblado es el de nuestro plugin, el cual ha compilado perfectamente. Seleccionamos la DLL correspondiente a nuestro plugin. Para buscar la DLL, le damos click a los 3 puntitos:


Una vez seleccionado el ensamblado, le damos click a "Register Selected Plugin", y si todo sale bien, aparece un cuadro de diálogo informándonos de dicha situación. Además, el plugin figura ahora en la lista de los plugins y workflows registrados.

Con ésto ya tenemos registrado el plugin, es decir, ya está "adentro" del CRM. Pero no hemos dicho cuándo queremos que se ejecute, por lo que ahora solo nos resta registrar un paso. Desplegamos el elemento de nuestro plugin y hacemos click derecho y "Register New Step":

Aparecerá una ventana así, o similar:


Aca detengámosnos un poco. El campo Message indica el "evento" sobre el cual queremos actuar. En este caso, yo seleccioné el evento de creación de una nueva entidad. Otra opción podría ser "Update", "Delete", etc.
El campo Primary Entity permite especificar la entidad sobre la que vamos a atender el evento de creación. Yo seleccioné "account" por ser una que viene por default. Y finalmente, indiqué que el plug-in se ejecute previamente a la validación (pre-validation) de los datos ingresados. Esto lo hice para evitar que me almacene siempre una nueva account, ya que yo solo quiero probar si funciona el plugin, no andar registrando una nueva cuenta cada vez. Aquí por supuesto elijan la opción más conveniente para su caso.

Finalmente, le damos a "Register New Step" y se registrará el paso. Con esto, ya podemos ir al CRM, y probar de crear una nueva cuenta y ver si el plugin se ejecuta, o no.

Un tip: el campo "Run in User´s Context" indica bajo cuál usuario el plugin se ejecutará. Con la opción "Calling User", con cualquier usuario que intente registrar una cuenta se ejecutará el plugin.

Espero que haya sido útil, y bienvenidos los buenos comentarios y sin spam! ;)

Saludos!

martes, 26 de mayo de 2015

Llamar a un Web Service desde un Plug-in de MS Dynamics

En este post vamos a ver como se hace para que un plug-in que hemos realizado nosotros mismos realice una llamada a un web service (que puede o no ser nuestro). Como siempre, estoy utilizando la versión de Dynamics 2015.
En este caso vamos a utilizar un web service que se pueda encontrar por Internet. Se trata de un conversor de unidades que encontré en la siguiente página:

http://www.webservicex.net/

El servicio en cuestión está en http://www.webservicex.net/ws/WSDetails.aspx?CATID=2&WSID=10, aunque si por algún motivo no se encuentra, no tienen más que buscar el servicio "Currency Convertor" desde la página principal. Ahora, si la página principal no funciona más, mala surte. Eso ya es algo que me trasciende.

Primero: Agregar la referencia al servicio.

Bien, primero lo primero: agregamos la referencia al servicio web haciendo click con el botón izquierdo sobre la carpeta "References" del proyecto, y después en "Add Service Reference".
Cuando se abre la vetana, vemos un cuadro de texto que dice "Adress". Bueno, allí vamos a copiar el WSDL del servicio.

El WSDL es un archivo XML que contiene el contrato del Web Service: qué hace, y con qué lo hace. Así podemos saber qué métodos tiene para ofrecernos, y qué parámetros requieren esos métodos. Ah, y también -lógicamente- que datos devuelven!
Para nosotros, esta URL es la siguiente:

http://www.webservicex.net/CurrencyConvertor.asmx?WSDL

Bien, una vez que ponemos la URL del WSDL del servicio que vamos a usar, le damos a "Go". Aparecerá una lista con los servicios que podemos utilizar. Abajo de la lista, hay una cuadro que dice "Namespace". Allí pondremos el nombre del namespace que "contedrá" el servicio después. Para nuestro caso, podría ser, por ejemplo: "WS_Curr" (que como habéis adivinado viene de "Web Service CURRency" ;)
Le damos aceptar.
Con esto, ya podemos usar los métodos y tipos que provee el web service sin que Visual Studio se enoje y lo subraye con rojo. La variable channel es la que está representando la instancia de nuestro web service.

Segundo: Instanciar el Web Service

Segundo lo segundo: creamos una factory utlizando un Endpoint y un Binding para crear canales de comunicación con el web service. En realidad, nosotros solo vamos a utilizar un canal.

BasicHttpBinding myBinding = new BasicHttpBinding();
myBinding.Name = "CurrencyConvertorSoap";
myBinding.Security.Mode = BasicHttpSecurityMode.None;
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
myBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
myBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

EndpointAddress endPointAddress = new EndpointAddress("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL");
ChannelFactory factory = new ChannelFactory(myBinding, endPointAddress);
CurrencyConvertorSoap channel = factory.CreateChannel();

Tercero (y último): Utilizar el servicio.

Esta es la parte más sencilla de explicar para mi, y entender para ustedes -o para mí si me olvido :)-
Para usarlo, se usa como se usaría cualquier método que está en otro namespace. Si queremos ver a cuantos pesos argentinos está el euro, no tenemos más que llamar al método adecuado, con los parámetros adecuados. Y en la segunda y última linea, mostramos una excepción desde el plug-in para ver si esto funcionó o no. He aquí:

double lalala = channel.ConversionRate(Currency.EUR, Currency.ARS);
throw new InvalidPluginExecutionException("No se si te interesa, pero el EURO está a: " + lalala.ToString());


Al ejecutarse este plug-in en MS Dynamics, deberíamos ver una excepción que mostrara el mensaje con el preciod el euro.
En definitiva, llamar a un web service desde un plug-in en Dynamics es algo extremadamente simple, como pueden ver. A continuación dejo el código completo del ejemplo, y sigue la promesa de en un próximo post mostrar como registrar un plug-in en Ms Dynamics.

using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace Plugin_Prueba
{
    public class Class1 : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));            
                                    
            BasicHttpBinding myBinding = new BasicHttpBinding();
            myBinding.Name = "CurrencyConvertorSoap";
            myBinding.Security.Mode = BasicHttpSecurityMode.None;
            myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
            myBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
            myBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

            EndpointAddress endPointAddress = new EndpointAddress("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL");
            ChannelFactory factory = new ChannelFactory(myBinding, endPointAddress);
            CurrencyConvertorSoap channel = factory.CreateChannel();

            double lala = channel.ConversionRate(Currency.EUR, Currency.ARS);
            throw new InvalidPluginExecutionException("No se si te interesa, pero el EURO está a: " + lala.ToString());
        }
    }
}


Saludos y bienvenidos los comentarios con buena onda -y sin spam!-

viernes, 22 de mayo de 2015

Escribir un Plug-In para MS Dynamics CMR 2015

Esta vez vamos  a escribir un poco de código. Hoy quiero mostrar cómo escribir un plug-in para ejecutar desde Microsoft Dynamics. Si bien yo estoy utilizando la version 2015, sirve para algunas anteriores, aunque desconozco exactamente hasta donde se aplica. Próximamente, en otra entrada del blog, voy a mostrar como registrar el plugin en MS Dynamics y asociarlo a algún evento particular.

Bien, la verdad es que crear un plug-in, en términos básicos, es muy, pero muy simple! De hecho vamos a comenzar con lo primero: agregar las referencias necesarias.

1. Primero lo primero.

Obviamente, comenzamos creando un proyecto. Este proyecto será simplemente una librería de clases (Class Library).

2. Agregar las referencias necesarias.

Lo segundo que tenemos que hacer es agregar las siguientes referencias:

  1. System.Runtime.Serialization
  2. System.ServiceModel
  3. Microsoft.Xrm.Sdk.dll
  4. Microsoft.Xrm.Client.dll
  5. Microsoft.Crm.Sdk.Proxy.dll

Las dos primeras se encuentran dentro de los ensamblados del framework. Las tres restantes debemos buscarlas dentro de la carpeta /bin de la carpeta descargada del "Dynamics CRM SDK".

3. Comenzar a escribir el plug-in.

Lo primero que vamos a tener en cuenta, es que la clase del plug-in debe implementar la interfaz IPlugin, el cual nos va a obligar a implementar el método Execute, cuyo código será ejecutado por el motor de ejecución de plug-ins dentro del CRM. En definitiva, tendremos esto:

public class Class1 : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {

        }
    }
Dentro de Execute escribimos nuestro plug-in. Por supuesto, podemos valernos de otras clases y métodos para implementar lo que querramos.

4. Firmar el Plugin

Este paso es escencial, de lo contrario ¡no podremos registrar el Plugin! Igualmente es fácil: Vamos a las propiedades del proyecto (Click izquierdo sobre el nombre del proyecto, y después en properties), y alli buscamos la pestaña "Signing". Checamos "Sign the assembly" y en "Choose a strong name key file" seleccionamos "New". Allí penemos un nombre cualquiera, pero SIN contraseña. Y listo, ya podemos compilar y registrar el plugin.

Trazas para la ejecución.

Bastante común es el caso de utilizar traces para hacer una traza de la ejecución. ¿Y ésto por qué? Porque un plug-in, como podréis imaginaros, es bastante difícil de debuggear. De hecho, la única forma es ejecutando es plug-in desde el CRM y mirando luego la traza de la ejecución. Para esto, creamos el servicio de traza con lo siguiente:

ITracingService tracingService;
tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));


y luego ir escribiendo algo que nos permita seguir la traza. Esto lo haremos llamando a:
tracingService.Trace("Paso 1");


Si intercalamos llamadas a tracingService.Trace("Paso X") dentro de nuestro código, podremos saber hasta dónde se ejecutó el plug-in. Si, por ejemplo falla, veremos que la traza nos muestras el lugar al que llegó antes de lazar la excepción. Acá muestro un ejemplo de traza:
using Microsoft.Xrm.Sdk;
using System;

namespace Plugin_Prueba
{
    public class Class1 : IPlugin
    {

        public void Execute(IServiceProvider serviceProvider)
        {

            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            tracingService.Trace("Paso 1");

            Entity entidad = (Entity)context.InputParameters["Target"];

            tracingService.Trace("Paso 2");

            if (entidad.Attributes.Contains("name") && entidad.Attributes["name"].ToString().Length > 5)
            {
                tracingService.Trace("Paso 3");
                throw new InvalidPluginExecutionException("Epa! El nombre tiene que tener más de 5 caracteres.");
            }
            tracingService.Trace("Paso 4");
        }
    }
}
Entonces, si durante la evaluación de la primera condición del if lanza una excepeción (si por ejemplo, el atributo "name" no existiera), se lanzará una excepción en el CRM y tendremos la opción de ver la traza. En la misma, figurara algo parecido a esto:
Paso 1
Paso 2
Pero no veremos "Paso 3" porque se lanzó la excepción y no se llegó a ejecutar esa parte. En la próxima entrada, veremos como registrar el plug-in para que el CRM lo pueda ejecutar. Es también algo muy sencillo.

Saludos!

miércoles, 31 de diciembre de 2014

¿Cómo testear métodos privados?

A la hora de hacer los unit testing, tarde o temprano nos llega indefectiblemente la pregunta: ¿cómo testeo un método privado?

El asunto en cuestión es motivo de distintos puntos de vista, opiniones, y discusiones. Sin embargo basado un poco en la experiencia personal y la ajena, más un poco de literatura, ofrezco mi punto de vista.

Existen dos opiniones diferentes al respecto: unos dicen que los métodos privados no deben testearse, y otros que si. Ah, y bueno, están los grises que dicen que "depende de la situación". Yo me incluyo en este último grupo. La ingeniería de software es una ciencia muy "gris" todavía.

La cruda realidad, es que con muchos de los frameworks no podemos testear métodos privados, por la simple razón de que son privados y no podemos acceder a ellos. (No lo dije antes, pero doy por hecho que los tests se encuentran en un proyecto aparte de aquel que contiene el código que estamos testeando y por eso no podemos accederlos).

Los que dicen que no deben testearse métodos privados, tienen sus razones. Principalmente sostienen que solo debe testearse la interfaz de, pongámosle, una clase. Es decir, todos aquellos métodos públicos que sirven de interfaz al mundo exterior (los programadores que utilizan estos métodos). Y que a su vez, los métodos privados son solo "ayudantes" de los métodos públicos. Estos pueden cambiar con mayor frecuencia que los métodos públicos, y por tanto, una eventual modificación de los métodos privados que no debiera alterar los resultados de los métodos públicos, podría hacer fallar los tests que testean los métodos privados. Es decir que se cambió el comportamiento interno sin alterar el comportamiento externo y que, por lo tanto, solo debemos preocuparnos por testear el comportamiento externo.

Yo no estoy precisamente de acuerdo con este punto de vista. Si queremos testear el método, deberíamos poder. La realidad es que así nomás no se puede. Entonces salen alguna alternativas que voy a mencionar, y me alegraría siempre de escuchar o leer nuevas.

1) Mover los métodos privados a una nueva clase.
Si el/los método/s privados que queremos testear lo ameritan, los refactorizamos creando una -o más- nueva clase que solo los contenga a ellos -pero como métodos públicos- y no formen parte de la interfaz de nuestra clase. De esta forma seguirán estando "ocultos" hacia el mundo exterior (porque no pertenecen a la interfaz), y sin embargo ya podemos accederlos tranquilamente desde el proyecto de testing.

2)  Convertir los métodos privados en públicos.
No soy un gran entusiasta de esta opción. Creo que si un método se pensó privado, privado debe permanecer. Si se cambia a público, la razón debería tener que ver con otras cuestiones y no con el testing. Pero ya dicho, es una opción más.

3) Convertir los métodos en internal.
Desconozco si existe en otros lenguajes el equivalente a internal de C#. Con esto permitimos que las clases dentro del mismo ensamblado se puedan ver. Sin embargo, fuera del ensamblado siguen permaneciendo ocultas. Y debemos especificar a qué ensamblado le damos visibilidad (con [assembly:InternalsVisibleTo("MyTests")]).

Cuando puede utilizarse, soy partidario de la tercera forma, ya que es bastante más limpia. En otro caso, habrá que evaluar qué hacer, o incluso si coincidir con quienes dicen que los métodos privados no tiene estrictamente que testearse.

Hasta pronto!