domingo, 17 de abril de 2016

No inicies desde cero, Yeoman te da una mano!


Para los desarrolladores es de suma importancia a la hora de iniciar un nuevo proyecto o la construcción de una nueva aplicación tener un punto de partida que nos indique una forma ideal de estructurar nuestro código y uso de buenas prácticas, particularmente los desarrolladores que solemos usar Visual Studio para la mayoría de nuestro proyectos estamos acostumbrados a encontrar este apoyo en las diversas plantillas o starters que VS nos ofrece, pero qué pasa si nos encontramos trabajando en un entorno diferente a Windows y no contamos con VS como IDE de desarrollo?



Pues una buena alternativa sin duda es el uso de Yeoman, una popular herramienta Open source de Scaffolding web, que cuenta con un amplio sistema de generadores, los cuales permiten obtener starters para diversos tipos de proyectos en diversas tecnologías que ofrecen un buen punto de partida, si quieres indagar un poco más sobre Yeoman te puedes remitir a su sitio oficial http://yeoman.io/ y aquí puedes encontrar su código fuente https://github.com/yeoman/yeoman



Yeoman actualmente cuenta con 3.738 generadores, entre los cuales encontramos algunos soportados por el equipo de Yeoman y otros por la comunidad, en estos generadores podemos encontrar starters para Angular, Ionic, Asp.Net Core, BootStrap, React y muchos más. Una característica bien importante de Yeoman es que se apalanca del uso de un Task runner (Gulp, Grunt, etc) y un gestor de paquetes (Bower, npm, etc) para sacarle el mayor provecho, dado que a través de Bower por ejemplo se pueden descargar las dependencias que tienen los diferentes generadores para lograr correctamente y a través de Grunt se pueden ejecutar tareas de compilación, concatenación, minificación, servir la aplicación, etc.

Ahora que ya conocemos un poco acerca de Yeoman, vamos a ver como lo podemos usar, empecemos por instalarlo:

En primer lugar vamos a instalar NodeJs, el cual puedes descargar de la siguiente página https://nodejs.org/en/ una vez lo estés instalando observarás que se instalará por defecto también npm, el gestor de paquetes de NodeJs, el cual nos permitirá instalar posteriormente, Yeoman, Bower y Grunt.

Ahora usaremos la consola de npm para instalar Bower, que recordemos nos servirá para instalar las dependencias de los starters que usemos con Yeoman, para esto ejecutamos el siguiente comando:

$ npm install -g bower

Ahora instalaremos Gulp, que es un Task Runner al igual que Grunt, podemos usar cualquiera de los dos, en este caso instalaremos Gulp, a través del siguiente comando:

$ npm install --global gulp-cli

 Y ahora a lo que vinimos, instalaremos Yeoman, a través del siguiente comando:

$ npm install -g yo

Para nuestro ejemplo vamos a crear una aplicación Asp.Net Core, por lo cual vamos a descargar su generador correspondiente, para iniciar vamos a escribir el comando "yo" en la ventana de comandos de Windows, de esta forma se nos presentarán algunas opciones a ejecutar


Si elegimos la opción instalar un generador, posteriormente nos pedirá digitar alguna palabra clave del nombre del generador para que se busque y se instale, puedes observar todos los generadores existentes y buscar más amigablemente en la siguiente página http://yeoman.io/generators/ Nosotros instalaremos directamente el generador de aplicaciones Asp.Net 5 a través del siguiente comando:

$ npm install -g generator-aspnet

Una vez termine de instalar el generador procedemos a crear nuestra aplicación Asp.Net 5 a través de este, para esto ejecutamos el siguiente comando:

$ yo aspnet

Para este generador en particular se nos preguntará que tipo de aplicación deseamos crear, aplicación de consola, Web, Web Api, etc


En nuestro caso seleccionaremos Web Application, y para finalizar debemos especificar el nombre que tendrá nuestra aplicación.

Con esto Yeoman genera la estructura de nuestro proyecto, y nos sugiere ejecutar los siguientes comandos para posicionarnos en la aplicación, restaurar los paquetes o dependencias, compilar y ejecutar respectivamente:


Si tenemos Visual Studio instalado, podemos abrir el proyecto a través de este IDE y seguirlo trabajando a través de él, pero en caso de que no lo tengamos podemos optar por los comando sugeridos anteriormente.

Como podemos ver en los comandos anteriores se hace uso de DNU y DNX, estos corresponden al .Net Execution Enviroment, que es el ambiente de ejecución que permite ejecutar las aplicaciones .Net Core, para lograr ejecutar estos comandos debemos instalar antes este entorno de ejecución, para eso usamos los siguiente comando, te recomiendo este post si quieres tener un poco más de contexto:

$ dnvm upgrade -r coreclr

Ahora que tenemos el ambiente de ejecución, ejecutaremos el siguiente comando para restaurar los paquetes o dependencias:

$ dnu restore

El siguiente comando para compilar el proyecto:

$ dnu build

Y para terminar el siguiente comando para ejecutar nuestra aplicación en un servidor local

$ dnx web

Y veremos el siguiente mensaje que nos indica en que url está corriendo nuestra aplicación para que la podamos probar:


Y si navegamos a través de un browser podremos ver nuestra aplicación corriendo con el starter por defecto de Asp.Net 5



Una vez tenemos nuestra aplicación Yeoman no nos deja solos en lo que viene, también a través de este podemos crear ítems específicos para nuestro proyecto, por ejemplo con el siguiente comando podríamos crear una nueva clase:

$ yo aspnet:Class ElTavoClase

Y bueno amigos con esto damos por terminado este artículo, sobre cómo usar Yeoman para iniciar nuestras aplicaciones, espero les haya resultado interesante y de utilidad.

No olvides darle me gusta mi página en Facebook para mantenerte actualizado de lo que pasa en el Tavo.Net https://www.facebook.com/eltavo.net

Saludos y buena suerte!

domingo, 3 de abril de 2016

Dapper vs EntityFramework, debo usar un MicroOrm o un Orm?


En muchos de nuestros proyectos hemos usado un Orm para lograr mapear nuestro modelo de datos a nuestros objetos y estructura de clases de nuestra aplicación, logrando con esto resolver el acceso y persistencia de datos sin mucho esfuerzo y poder enfocarnos en lo que verdaderamente nos importa, el dominio del negocio.



Los Orm han tomado mucha fuerza y actualmente son usados en la mayoría de aplicaciones dado las facilidades y bondades que ofrecen, Orms como Nhibernate, EntityFramework, Hibernate, Telerik Data Access, son ejemplos de Orms bastante populares y usados en la actualidad, debido a las facilidades que ofrecen y buenas prácticas y patrones que encapsulan.

Pese a esto siempre se ha discutido con respecto al rendimiento que estos nos ofrecen y cómo pueden afectar el desempeño de nuestras aplicaciones, debido a las facilidades y funcionalidades que ofrecen se vuelven bastante robustos para preparar y acceder a la información. Por ejemplo EntityFramework está basado sobre la arquitectura de Ado.Net lo cual sugiere que tenemos una capa intermedia, que obtiene el resultado a través de los componentes de este y posteriormente mapea el resultado a un objeto de nuestra aplicación, este proceso sugiere algunos milisegundos más en cada transacción.

Ahora, que pasa si tuviéramos una alternativa un poco más liviana y rápida que sacrifique algunas facilidades y características para garantizar el rendimiento en el acceso a datos? pues de esto se trata un MicroOrm, este nos permite realizar un mapeo de nuestro modelo relacional a objetos de nuestra aplicación, solo que lo hace de una forma más simple y sin algunas características como por ejemplo manejo de relaciones entre tablas, manejo de concurrencia, cargas por demanda, diseñador del modelo, etc.

Entre los MicroOrm más famosos encontramos los siguientes:
  • PetaPoco
  • Insight
  • NPoco
  • Dapper (Se puede decir que este es el MicroOrm más conocido y usado, debido a que se ha comprobado que es el que mejor rendimiento tiene, tan solo un milisegundo más que una consulta directamente con Ado.Net, adicional este es el MicroOrm usado por el reconocido foro StackOverflow, para soportar su robusta arquitectura)
Ahora que ya conocemos que es un micro Orm y sabemos cuál es su principal diferencia con un Orm, vamos a ver cómo funciona y cómo es su implementación, para esto vamos a usar Dapper, y adicional vamos a realizar una serie de comparaciones de rendimiento y de codificación con EntityFramework.

Para nuestro ejemplo vamos a usar la conocida base de datos Northwnd, la cual puedes descargar del siguiente enlace: https://northwinddatabase.codeplex.com/

Posteriormente vamos a crear en Visual Studio un nuevo de proyecto de tipo consola y en la clase Program, vamos a empezar a codificar el ejemplo.

Para iniciar vamos a abrir la consola del administrador de paquetes Nuget y vamos a ejecutar el siguiente comando para instalar el paquete de Dapper:

Install-Package Dapper -Version 1.42.0

Y hacemos el Using del NameSpace Dapper en la clase Program, con esto se agregan una serie de métodos de extensión a la clase SqlConnection las cuales nos permiten usar el MicroOrm para interactuar con la base de datos.

Para la conexión a la base de datos a través de Dapper solo necesitamos especificar la cadena de conexión, para esto crearemos una constante en clase Program en la cual la almacenaremos, para efectos de nuestro ejemplo vamos a usar cadenas mágicas en el código y no archivos de configuración, dado que esto no es relevante para el ejercicio:

private const string Cadena = "Initial Catalog=NORTHWND;Data Source=TAVOPC;Integrated Security=SSPI;";

Escenario de una consulta simple a una sola tabla

Ahora vamos a codificar nuestro primer escenario, en el cual vamos a consultar en la tabla Ordenes, a través de Dapper y vamos a mapear el resultado a la entidad Orden (entidad creada por nosotros)

private static void ConsultarOrdenesDapper()
{
    using (IDbConnection db = new SqlConnection(Cadena))
    {
        var consulta = @"SELECT [OrderID] AS Id, [CustomerID] AS IdCliente, [EmployeeID] AS IdEmpledo,
                    [OrderDate] AS Fecha, [RequiredDate] AS FechaRequerida, [ShipCountry] AS Pais, [ShipCity] AS Ciudad
                    FROM [NORTHWND].[dbo].[Orders]";
 
        var stopwatch = new Stopwatch();
        stopwatch.Start();
 
        // Se ejecuta la consulta.
        var ordenes = db.Query<Orden>(consulta);
 
        stopwatch.Stop();
        System.Console.WriteLine("Dapper.Net: {0} registros obtenidos en {1} milisegundos.", ordenes.Count(),
          stopwatch.ElapsedMilliseconds);
    }
}

Como podemos ver es bastante sencillo usar Dapper, en este caso a través de la clase SqlConnection establecemos la conexión con la base de datos, y ejecutamos el método Query, especificando el tipo en el que queremos obtener el resultado de la consulta, cabe mencionar que el método Query no se encuentra en la clase SqlConnection por defecto, este es un método de extensión agregado por el Nuget de Dapper. Ahora invocaremos el método ConsultarOrdenesDapper desde el método Main de la clase program, y ejecutamos el programa para ver los resultados.


Como podemos apreciar obtenemos un resultado bastante óptimo de 856 registros en 4 milisegundos, ahora en cuánto tiempo resolverá EntityFramework esta consulta? pues vamos a averiguarlo.

Para crear nuestro modelo conceptual a través de EntityFramework, vamos a usar el enfoque "Code First From DataBase" el cual permite usar CodeFirst con una base de datos existente y crea el contexto y las entidades a partir de ella, observa aquí un tutorial detallado acerca de cómo usar este enfoque: CodeFirst From DataBase

Ahora que ya tenemos nuestro contexto vamos a codificar la misma consulta que realizamos con Dapper, pero esta vez la ejecutaremos a través de EntityFramework.

private static void ConsultarOrdenesEf()
{
    using (var contexto = new NorthwndModel())
    {
      var stopwatch = new Stopwatch();
      stopwatch.Start();
 
      var ordenes = contexto.Orders.ToList();
 
      stopwatch.Stop();
      System.Console.WriteLine("EntityFramework: {0} registros obtenidos en {1} milisegundos.", ordenes.Count,
      stopwatch.ElapsedMilliseconds);
    }
}

Ahora de igual forma vamos a invocar el método ConsultarOrdenesEf desde el método main de la clase Program, y ejecutamos de nuevo la aplicación para ver el resultado:


Pues la diferencia es bastante grande de 6 milisegundos de Dapper a 95 milisegundos de EntityFramework, sin lugar a dudas es una diferencia significativa, si bien 95 milisegundos siguen siendo poco esos milisegundos van sumando en cada transacción, y en una aplicación de alta demanda por ejemplo sí que se notarían y afectarían muy negativamente el rendimiento.

Ahora que hemos realizado este primer escenario comparativo podemos concluir que definitivamente el rendimiento de Dapper es muy superior al de EntityFramework, dado que como había mencionado antes el MicroOrm es muy rápido y liviano, por el contrario el Orm es bastante robusto y ofrece muchas más extensiones y funcionalidades que penalizan un poco el rendimiento. Sin embargo si analizamos y comparamos detenidamente el código de cada una de las implementaciones podemos decir que la implementación de EntityFramework es mucho más limpia, trazable y mantenible, otro aspecto muy importante en nuestras aplicaciones. Adicional vale la pena considerar el escenario en el cual trabajamos con tablas relacionadas, escenario que encontraremos en todas nuestras aplicaciones y que veremos a continuación.

Escenario de consulta a dos tablas relacionadas

Sin duda una de esas características que brilla por su ausencia en los MicroOrm con respecto a los Orm, es el manejo de relaciones entre entidades de una forma sencilla y objetual, obviamente el Micro Orm no tiene esto entre sus prioridades, pero si que hace falta. Para plantear este escenario vamos a realizar una consulta en la tabla ordenes y en la tabla ordenes detalle, en un escenario Eaguer, es decir donde necesitamos obtener la información en una sola consulta a la base de datos y no por demanda, vemos como sería la implementación con Dapper:

private static void ConsultarOrdenesDetallesDapper()
{
            var consulta = @"SELECT a.[OrderID] AS Id
	                    ,a.[CustomerID] AS IdCliente
	                    ,a.[OrderDate] AS Fecha
                          ,b.[ProductID] AS IdProducto
                          ,b.[UnitPrice] AS Precio
                          ,b.[Quantity] AS Cantidad
                          ,b.[Discount] AS Descuento
                      FROM [NORTHWND].[dbo].[Order Details] b
                      INNER JOIN [NORTHWND].[dbo].[Orders] a
                      ON a.OrderID = b.OrderID;";
 
            using (IDbConnection db = new SqlConnection(Cadena))
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
 
                // Se ejecuta la consulta.
                var ordenesDetalle = db.Query<DetalleOrden>(consulta);
 
                stopwatch.Stop();
                System.Console.WriteLine("Dapper.Net: {0} registros obtenidos en {1} milisegundos.",
                    ordenesDetalle.Count(), stopwatch.ElapsedMilliseconds);
            }
}

De la implementación anterior podemos observar que se hace una consulta involucrando las dos tablas y se traen todos los registros de ambas, es decir se repiten los registros de la tabla ordenes por cada registro asociado en la tabla ordenesDetalle, y debido a esto se creó la entidad DetalleOrden, lo cual es una implementación que no nos permite orientar correctamente a objetos, ya que deberíamos tener la entidad orden y la entidad detalle separadas y cargarlas con la data, esto se debe a que el MicroOrm no permite manejar las relaciones y esta es una solución al escenario que planteamos de carga total. Ahora veamos como logramos la misma implementación pero usando EntityFramework.

private static void ConsultarOrdenesDetallesEf()
{
      using (var contexto = new NorthwndModel())
      {
         var stopwatch = new Stopwatch();
         stopwatch.Start();
 
         var ordenesDetalle = contexto.Orders.Include("Order_Details").ToList();
 
         stopwatch.Stop();
         .Console.WriteLine("EntityFramework: {0} registros obtenidos en {1} milisegundos.",
         ordenesDetalle.Count, stopwatch.ElapsedMilliseconds);
      }
}

De nuevo podemos observar a simple vista que la implementación con EntityFramework es mucho más limpia y orientada a objetos, hacemos una consulta sobre la tabla Ordenes y especificamos que se incluyan los datos de la tabla relacionada o propiedad de navegación Order_Details, de esta forma se obtienen los registros:


Todos los datos en sus respectivas entidades de una forma objetual, ahora veamos el rendimiento de cada de una de las implementaciones:


Sigue existiendo una diferencia abismal en el rendimiento de las consultas, y ahora que EntityFramework está gestionando las relaciones ha aumentado sustancialmente, dado que debe mapear las entidades y cada registro de las entidades de una forma anidada.

Escenario de inserción en una sola tabla

Ahora vamos a ver un par de escenarios en los cuales no consultaremos información si no que la guardaremos, para observar cómo se comportan tanto el MicroOrm como el Orm, veamos la implementación para Dapper y para EntityFramework:

private static void InsertarOrdenDapper()
{
    var consulta =
                $@"INSERT INTO [NORTHWND].[dbo].[Orders] (CustomerID, OrderDate) 
                    VALUES ('VINET', '{DateTime.Now.ToString("yyyy-MM-dd")}');";
    using (IDbConnection db = new SqlConnection(Cadena))
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
 
        // Se ejecuta la instrucción.
        db.Execute(new CommandDefinition(consulta));
 
        stopwatch.Stop();
        System.Console.WriteLine("Dapper.Net: un registro insertado en {0} milisegundos.",
        stopwatch.ElapsedMilliseconds);
    }
}

private static void InsertarOrdenEf()
{
    using (var contexto = new NorthwndModel())
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
 
        var orden = new Order
        {
            CustomerID = "VINET",
            OrderDate = DateTime.Now
        };
 
        contexto.Orders.Add(orden);
        contexto.SaveChanges();
 
        stopwatch.Stop();
        System.Console.WriteLine("EntityFramework: un registro insertado en {0} milisegundos.",
                    stopwatch.ElapsedMilliseconds);
    }
}

Como podemos ver dos implementaciones muy sencillas en Dapper y en EntityFramework para insertar un registro, ahora comparemos el rendimiento de ambas implementaciones:


En este escenario de inserción de un solo registro nos damos cuenta que en este aspecto la diferencia de rendimiento entre Dapper y EntityFramework no es tan grande, solo 7 milisegundos una diferencia en tiempo realmente pequeña.

Escenario de inserción en dos tablas relacionadas

Para terminar la comparación entre Dapper y EntityFramework vamos a ver el último escenario en el cual vamos a insertar información en dos tablas relacionadas, veamos entonces como es el código a implementar para cada tecnología:

private static void InsertarOrdenDetalleDapper()
{
      var consulta =
        $@"INSERT INTO [NORTHWND].[dbo].[Orders] (CustomerID, OrderDate) 
        VALUES ('VINET', '{DateTime.Now.ToString("yyyy-MM-dd")}') select SCOPE_IDENTITY();";
      using (IDbConnection db = new SqlConnection(Cadena))
      {
          var stopwatch = new Stopwatch();
          stopwatch.Start();
 
          // Se almacena la orden y se obtiene su Id Identity.
          var idOrden = (int)db.Query<decimal>(consulta).First();
 
          consulta = $@"INSERT INTO [NORTHWND].[dbo].[Order Details] 
          VALUES ({idOrden}, 41, 20, 100, 0);";
          db.Execute(new CommandDefinition(consulta));
 
          stopwatch.Stop();
          System.Console.WriteLine("Dapper.Net: registros relacionados insertados en {0} milisegundos.",
          stopwatch.ElapsedMilliseconds);
      }
}

private static void InsertarOrdenDetalleEf()
{
    using (var contexto = new NorthwndModel())
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
 
        var orden = new Order
        {
            CustomerID = "VINET",
            OrderDate = DateTime.Now,
            Order_Details =
            {
                new Order_Detail
                {
                    ProductID = 41,
                    UnitPrice = 20,
                    Quantity = 100,
                    Discount = 0
                }
            }
        };
 
        contexto.Orders.Add(orden);
        contexto.SaveChanges();
 
        stopwatch.Stop();
        System.Console.WriteLine("EntityFramework: registros relacionados insertados en {0} milisegundos.",
        stopwatch.ElapsedMilliseconds);
    }
}

Como podemos observar en este último escenario para EntityFramework creamos un solo objeto, especificando la información para la entidad padre y también para para la propiedad de navegación y EntityFramework se encarga de ejecutar las dos instrucciones de inserción, por el contrario con Dapper se debe ejecutar en primera instancia la instrucción para la tabla padre y obtener su Id, para posteriormente ejecutar la instrucción de la tabla relacionada, ahora observemos el desempeño de ambas implementaciones:


Como en el escenario anterior vemos que la diferencia no es muy significativa, cuando se trata de operaciones de inserción. Dados los anteriores escenarios podríamos concluir que Dapper tiene un rendimiento muy superior a EntityFramework en cuanto a consulta de datos se refiere, sin embargo en operaciones de inserción tienen un rendimiento muy similar. También podemos concluir que usando un Orm podemos lograr implementaciones mucho más limpias y objetuales y un código más mantenible.

En resumidas cuentas podemos concluir que los Micro Orm nos permiten practicidad, simplicidad y óptimo rendimiento, sacrificando un poco la mantenibilidad y orientación a objetos, mientras que los Orm nos permiten mejor mantenibilidad y orden sacrificando un poco el rendimiento, elegir uno u otro siempre dependerá del escenario que estemos resolviendo y según lo requieran nuestro atributos de calidad, recordemos que en el desarrollo de software no existen las balas de plata, y ambas tecnologías pueden ser muy válidas en determinados escenarios.

Y bueno amigos con esto damos por terminado el artículo, espero que esta comparación entre Dapper y EntityFramework les haya resultado interesante y de utilidad.

El código fuente lo puedes descargar de mi repositorio en GitHub.



No olvides visitar mi página en Facebook para mantenerte actualizado de lo que pasa en el Tavo.Net https://www.facebook.com/eltavo.net

Saludos, y buena suerte!