martes, 21 de mayo de 2013

Autocrítica



Hace unas semanas platicaba con mis alumnos acerca de la autocrítica que debe existir para poder entregar un trabajo no perfecto pero sí aceptable, creo que la calidad en el proceso y el resultado depende mucho del nivel de autocrítica que uno pueda tener.

Es difícil darse cuenta de los errores que comete uno mismo, es difícil que estás fallando en algo si es que no sabes que tienes el error por lo que resulta muy complicado el prever problemas cuando la falta de conocimiento es la fuente, ahí creo que la única solución es la investigación y el autoaprendizaje.

Existe un punto muy interesante cuando un programador va a entregar un trabajo, la certidumbre de que se hizo todo lo posible por entregar algo de calidad y haber realizado las pruebas necesarias.

¿Nunca te ha pasado que cuando entregas el trabajo tienes esa sensasión de "ojalá nada falle"?, esa cosquilla en la panza que pide a gritos que no te vayan a regañar o el cliente no se vaya a poner fúrico por un pantallazo de error.

En el momento en que llegas a tener esa sensación debes detenerte y pensar si estás poniendo todo de tu parte para que el resultado sea tal como se desea, la incertidumbre es una señal inequívoca de un trabajo incompleto o de un esfuerzo a medias; si este es el sentimiento, podemos estar seguros que las cosas no se están haciendo como deberían.

Hacer pruebas es de las cosas que a un programador le resultan más tediosas, es un común denominador que las pruebas de integridad o de unidad se sienten como una parte inútil cuando sientes que tienes la habilidad suficiente y comprobable como para evitarlas, recordemos que hasta el mejor cazador se le va la liebre, por algo existen estas pruebas, podemos amparar un buen trabajo respecto a las pruebas que se diseñaron antes de hacer el trabajo, donde éstas cubren los datos de entrada y de salida de nuestros componentes o sistema.

¿Queremos un México con sistemas de calidad?, no hay de otra más que poner el esfuerzo necesario para cumplir con las espectativas, y superarlas si es posible.

Recomiendo ampliamente utilizar las herramientas que tiene Visual Studio para hacer pruebas de unidad e integración, o bien apoyarnos de herramientas externas como nUnit.

Te recomiendo esta página de MSDN para la creación y ejecución de de pruebas de unidad.

En unos días publicaré una entrada donde mostraré cómo crear una prueba de unidad utilizando los proyectos de prueba de .NET

EntityFramework en WCF



La entrada de hoy va a romper un poco la secuencia que venía publicando abordando temas de programación en SQL Server (específicamente de XML), pero hoy que estaba preparando mi curso 70-513 encontré un tema sumamente interesante, al menos a mí me lo parece porque me trabajo encontrar la solución en su momento.

Comencemos por establecer que tendremos 4 proyectos:
  1. Modelo de EntityFramework de una base de datos.
  2. Biblioteca de clases donde se encuentra definido e implementado nuestro servicio WCF.
  3. Aplicación de consola que aloja y expone nuestro servicio WCF.
  4. Aplicación de consola que utiliza el servicio expuesto por la aplicación de consola.
He decidido crear una biblioteca de clases para implementar el servicio para que la implementación sea totalmente independiente del alojamiento de la misma, es decir que esa misma biblioteca de clases la podríamos llevar a WAS, a un servicio de windows, a una aplicación de windows, etc.

Para obtener el script que genera la base de datos basta con dar clic en el siguiente enlace: script para crear base de datos de prueba.

Si le echamos un ojo al script veremos que la tabla Categorias tiene una referencia circular para generar una estructura jerárquica y que la tabla de ProductosRelacionados es básicamente una tabla intersección entre Productos y Productos para establecer los productos relacionados.

Esta entrada primero la voy a llevar por el camino que uno normalmente llevaría para enviar los objetos del EF en un servicio WCF y posteriormente haremos las modificaciones necesarias para corregir el problema que encontraremos.

Vamos a crear una solución en blanco y la llamaremos EFWCF, es importante anotar que tenemos que ejecutar el Visual Studio 2010 como administrador.    Esto es para evitar el uso necesario de netsh para asignar un puerto en particular a nuestra host del servicio WCF.

Vamos a agregar un proyecto de tipo Class Library a nuestra solución y la llamaremos EFWCFDB:
  1. Borramos el archivo Class1.cs que agrega por defecto el template del proyecto.
  2. Vamos a agregar un nuevo elemento de tipo ADO.NET Entity Data Model y lo llamaremos EFWCF.
  3. Elegimos crear el modelo desde una base de datos.
  4. Creamos una nueva conexión que apunte hacia nuestra base de datos llamada EFWCF, utilizaremos autenticación de Windows para efectos de prueba.
  5. Damos clic en Next, elegimos las tres tablas de la base de datos y activamos la casilla de verificación que dice "Pluralize or singularize generated object names" y damos clic en Finish.
El IDE nos presentará un modelo como el que muestro en la siguiente imagen:


Vamos a modificarlo un poco para darle más significado a las entidades y realizaremos los siguientes ajustes:
  1. Renombrar la propiedad Categoria.Categorias1 por CategoriasHijas.
  2. Renombrar la propiedad Categoria.Categoria1 por CategoriaPadre.
  3. Renombrar la propiedad Producto.Productos1 por ProductosRelacionados.
  4. Eliminar la propiedad Producto.Productos dado que su contenido ya está cubierto por la propiedad que acabamos de renombrar.
Con estos cambios nuestro modelo deberá quedar de la siguiente manera:


Con esto podemos dar por terminado nuestro modelo (por el momento) y ahora vamos a crear nuestro proyecto que tendrá la implementación del servicio WCF.

Vamos a agregar un nuevo proyecto a nuestra solución de tipo Class Library y lo llamaremos EFWCFServices:
  • Eliminamos el archivo Class1.cs que trae por defecto la plantilla de este tipo de proyectos.
  • Agregamos un nuevo elemento de tipo WCF Service y lo llamamos EFWCFService.
  • Abrimos el archivo IEFWCFService.cs y eliminamos el procedimiento DoWork con todo y su atributo OperationContract.
  • Abrimos el archivo EFWCFService.cs y eliminamos el procedimiento DoWork.
  • Agregamos referencia al proyecto EFWCFDB.
  • Agregamos referencia a System.Data.Entity.
  • Vamos a agregar una operación a IEFWCFService.cs con la siguiente firma: List<EFWCFDB.Producto> ProductosPorCategoria(int idCategoria)
  • Ajustamos nuestra implementación (EFWCFService.cs) de manera tal que quede así el código:
public class EFWCFService : IEFWCFService
{
    public List<EFWCFDB.Producto> ProductosPorCategoria(int idCategoria)
    {
        using (EFWCFDB.EFWCFEntities db = new EFWCFDB.EFWCFEntities())
        {
            return db.Productos.Where(p => p.Id_Categoria == idCategoria).ToList();
        }
    }
}

Ahora vamos a crear el proyecto que se utilizará como host de nuestro servicio, agreguemos a la solución un proyecto de tipo Console Application y lo llamaremos EFWCFHost.
  1. Vamos a agregar referencia a System.ServiceModel y a nuestro proyecto EFWCFServices.
  2. Agregamos un nuevo elemento de tipo Application Configuration File y lo dejamos con el nombre que trae por defecto.
  3. Vamos a registrar nuestro servicio desde el archivo app.config con el código que he colocado en esta liga: app.config para EFWCFService.    Es importante hacer un ajuste, en system.serviceModel - services - service - host - baseAddresses - add se hace referencia a una dirección que apunta hacia mi computadora, en tu caso deberás modificar esa dirección para que haga referencia al nombre de tu equipo.
  4. Una vez registrado el servicio en el app.config vamos a crear nuestro host en el procedimiento main del archivo Program.cs de nuestro proyecto con el siguiente código: código para host de EFWCFService.
  5. Hay que ejecutar el proyecto EFWCFHost sin depuración, esto es para dejarlo en ejecución y posteriormente agregar una referencia al servicio alojado en él.    Entonces, lo ejecutamos con Ctrl + F5 y lo dejamos corriendo, deberá aparecer una pantalla como esta:

Finalmente vamos a agregar nuestro proyecto cliente el cual será de tipo Console Application y lo llamaremos EFWCFClient.
  1. Agregamos referencia a System.Data.Entity.
  2. Damos clic derecho sobre el proyecto en el Solution Explorer y elegimos Add Service Reference.
  3. En la pantalla que sale vamos a apuntar la dirección base que se configuró en el archivo app.config de la aplicación EWCFHost, en mi caso es http://JorgeToriz-PC:8001/EFWCFServices.
  4. Al dar clic en el botón Go aparecerá nuestro servicio, damos clic en él y le damos el namespace EFWCFServices.
  5. En el procedimiento main de nuestro Program.cs vamos a colocar el siguiente código para mandar a llamar a nuestro servicio
EFWCFServices.EFWCFServiceClient client = new EFWCFServices.EFWCFServiceClient();
Console.WriteLine("Categoría 2: " + client.ProductosPorCategoria(2).Length.ToString());
En la base de datos hay tres productos en la categoría 2, pero está provocando un error debido a las referencias circulares que tenemos en nuestro modelo en el proyecto EFWCFDB.

El mensaje de error es como este: "An error occurred while receiving the HTTP response to http://jorgetoriz-pc:8001/EFWCFServices/v1/EFWCFService.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down)".

¿La solución?, deshabilitar la propiedad "Lazy Loading Enabled" de nuestro modelo, vamos a abrir el archivo EFWCF.edmx que está en el proyecto EFWCFDB, una vez abierto damos clic sobre el fondo, vamos a las propiedades y cambiamos la propiedad de "true" a "false".

Guardamos cambios y compilamos el proyecto EFWCFServices (que mandará a compilar el EFWCFDB por tener referencia en la solución), detenemos nuestro servicio (EFWCFHost) y lo volvemos a arrancar, al ejecutar el cliente el error habrá desaparecido y mostrará algo como esto:


Si queremos que en la respuesta del servicio se incluya la referencia circular, lo deberemos implementar con la función Include de la siguiente manera (proyecto EFWCFServices - EFWCFServices.cs):

return db.Productos.Include("ProductosRelacionados").Where(p => p.Id_Categoria == idCategoria).ToList();

Si depuramos nuestro cliente veremos que ya aparecen los productos relacionados (propiedad ProductosRelacionados) con los productos que formaron parte del resultado, estos datos estarán vacíos por haber deshabilitado la propiedad "Lazy Loading Enabled" del modelo.

Espero te haya resultado de utilidad esta entrada.