MSDN Blogs
  • WarNov Developer Evangelist

    DynamicTableEntity: Azure Storage con Entidades Dinámicas

    Recuerdo por allá en la prehistoria de Azure cuando pocos éramos los que nos aventurábamos en esas aguas inexploradas, que cuando uno quería usar el Azure Storage y en especial las tablas, tenía que generar toda una capa de acceso a datos con la parafernalia que ello conlleva para poder usar el api que en esa época se llamaba Microsoft.WindowsAzure.StorageClient.dll. Hasta la versión 1.7 se llegó con este acercamiento. A uno le tocaba SIEMPRE crear clases correlativas a las tablas que estaban en el storage y además acoplarlas a la librería lo que hacía difícil las pruebas unitarias. Adicionalmente había que crear los wrappers para hacer operaciones CRUD.

    Luego vino el revolcón de la versión 2.0 del storage API que se identifica con el cambio de nombre a Microsoft.WindowsAzure.Storage.dll y después de esto, el fuerte encaminamiento que tomamos en Microsoft para ir a toda velocidad alcanzando los estándares y tendencias de la industria, obviamente con un trabajo impecable. Es así como hoy en día con el dominio del dinamismo y temas como nodejs y demás, ya vamos en la version 4 del api. Mantenemos el nombre desde la versión 2 porque los cambios no han sido tan “breaking”. Pero sí han traído cosas maravillosas como las DynamicTableEntity.

    Gracias a ellas, podemos trabajar con las Azure Tables desde el api para .net sin necesidad de mapearlas al estilo ORM como antaño era requerido. Solo basta entender un poco las abstracciones que hemos hecho sobre el storage, para hacer los llamados correctos.

    Es mi intención entonces en este post, mostrar la manera AGIL y DINAMICA de acceder al storage, en este caso, desde una sencilla aplicación de escritorio.

    Lo primero que hay que hacer es importar el api. Agrega una referencia a Microsoft.WindowsAzure.Storage.dll. Ojo; no uses Microsoft.WindowsAzure.StorageClient.dll porque es la versión antigua que no contiene la magia que voy a mostrar ahora.

    Del api, vamos a necesitar puntualmente dos Namespaces:

    using Microsoft.WindowsAzure.Storage;
    using Microsoft.WindowsAzure.Storage.Table;

    Acto seguido, creamos la conexión. Lo haremos “manualmente” sin necesidad de agregar más pasos por ejemplo a través del uso del CloudConfigurationManager, para mantenerlo todo más simple. Si vamos a usar el DevelopmentStorage, en este post muestro cómo hacer lo mismo.

    CloudStorageAccount _account = CloudStorageAccount.Parse(
       String.Format("DefaultEndpointsProtocol=https;AccountName={0};"
    "AccountKey={0}",
    "tucuenta",
    "45JHDAMEegL7G7wlCZhZ60UYNltbj6bZiH9x3eLHNSD1TEIQ+jw=="));

    Luego de esto, procedemos a crear un cliente de tablas. Este cliente, nos permitirá hacer todas las operaciones que requiramos con ella. Así hay clientes para blobs y colas. Anteriormente, todo esto quedaba dentro del mismo Namespace. Hoy en día, como vemos, tenemos un Namespace separado para cada elemento del storage de Azure. Aquí hemos incluido obviamente el Namespace de tablas.

    CloudTableClient _tableClient = _account.CreateCloudTableClient();

    Ahora supondremos que estamos trabajando sobre una cuenta de storage que ya tiene data probablemente obtenida a través de otra app y que lo que haremos es consultar dicha data. Usando Cloud Storage Studio:

    image

    Así pues, debemos obtener una referencia a esa tabla para poder trabajar con ella. Dicha referencia se obtiene a través del cliente que creamos:

    CloudTable _tblInventory = _tableClient.GetTableReference("Inventory");

    Ahora; cómo consultar dicha tabla sin tener una clase/entidad que la represente dentro de nuestro proyecto?

    Hay varios tipos de consultas sobre el storage de Azure. Podemos por ejemplo traer todos los elementos dentro de una partición dada, solo un subconjunto de elementos dentro de una partición, o solo un elemento obviamente especificando la PartitionKey y la RowKey. En este artículo encuentran una descripción de todas las operaciones que se pueden ejecutar sobre el storage. Solo que allí no se muestra cómo se puede operar sin necesidad de crear las entidades de mapeo. Y ese precisamente es el objeto de este post. Así que todas las operaciones indicadas allí, se pueden hacer dinámicamente, si siguen la metodología que describo aquí.

    Entonces para nuestra ilustración requeriremos hacer una consulta de todos los laptops que vende nuestra ficticia tienda de tecnología en la que en la tabla inventario tenemos como PartitionKey la categoría del elemento de tecnología, por ejemplo: compute, mobile, videogames, etc. En el RowKey tendríamos entonces la referencia de cada producto y además tendríamos entre muchas otras propiedades, la SubCategory del producto; por ejemplo, en la categoría cómputo, tendríamos subcategorías como laptop, PC, server, etc. Estamos interesados en laptops.

    Así que lo que queremos es seleccionar todos los elementos cuya PartitionKey=”Compute” y cuya Subcategory=”laptop”.

    Iniciemos entonces armando los filtros usando el método estático GenerateFilterCondition de TableQuery que trabaja así:

    (valorIzquierdo, operadorComparativo, valorDerecho):

    string categoryFilter = TableQuery.GenerateFilterCondition(
        "PartitionKey",
        QueryComparisons.Equal,
        "Compute");
    
    string subcategoryFilter = TableQuery.GenerateFilterCondition(
        "SubCategory",
        QueryComparisons.Equal,
        "Laptop");

    Ahora debemos combinar estos filtros con un “AND”, de acuerdo a nuestros requerimientos. Para esto usamos el método estático CombineFilters de la clase TableQuery que recibe dos filtros y un operador lógico.

    (Filtro1, Operador, Filtro2):

     string combinedFilters = TableQuery.CombineFilters(
         categoryFilter,
         TableOperators.And,
         subcategoryFilter);

    Como se puede ver, todo esto es mero texto. Pero usando estos metoditos nos ahorramos posibles errores al manipular mucho texto, como cuando escribíamos comandos de “T-SQL” para ejecutar por allá en los tiempos del ADO.NET. De hecho, el filtro resultante es:

    (PartitionKey eq 'Compute') and (SubCategory eq 'Laptop')

    Que si ponemos en una herramienta como Cloud Storage Studio nos da:

    image

    Todo esto porque nos basamos en el estándar de WCF Data Services Filters.


    Ahora usamos todo eso para armar una instancia de TableQuery que luego una tabla (tblInventory) ejecutará, obteniendo un conjunto IEnumerable de precisamente DynamicTableEntity, donde cada elemento es un registro coincidente con la consulta:

    TableQuery query = new TableQuery().Where(combinedFilters);
    IEnumerable<DynamicTableEntity> virtualResults = 
        _tblInventory.ExecuteQuery(query);

    Hasta aquí solo tenemos la definición del conjunto de datos que queremos y esto es Lazy; o sea que aún no se ha ejecutado contra el servidor, pues esto solo sucede cuando en realidad iteremos sobre los elementos. Por ejemplo si convertimos esto a una lista de DynamicTableEntity:

    List<DynamicTableEntity> laptops = virtualResults.ToList();

    Ahora supongamos que queremos mostrar en la consola las referencias de cada uno de los elementos filtrados (laptops) y la existencia actual. Entonces sencillamente podríamos escribir:

    foreach(var laptop in laptops)
    {
        Console.WriteLine(
            String.Concat(
                laptop.RowKey,
                "\t",
                laptop["StockAmount"].Int32Value));
    }

    Obsérvese que las propiedades nativas de las tablas de Azure como PartitionKey, RowKey, ETag, y Timestamp vienen fuertemente tipadas en una DynamicTableEntity, mientras que a las demás debemos acceder a manera de diccionario, además teniendo la facilidad de elegir el tipo de dato que vamos a recibir.

    Al final obtenemos:

    image

    De esta manera tendríamos cubierta la consulta de elementos tablas de Azure de manera dinámica.

    Ahora; cómo se haría una inserción o un update?

    Para explicar ambas cosas usaré la operación InsertOrReplace que ejecutará nuestra tabla. En este caso, primero necesitaré ejecutar la operación Retrieve para ver si el elemento que busco ya existe y modificarlo, o si no, crear uno nuevo.

    TableOperation retrieve = TableOperation.Retrieve("Compute", "X220");
    TableResult retrievedLaptop = _tblInventory.Execute(retrieve);
    //Tenemos que hacer un casting a DynamicEntity para trabajar ágilmente
    //El resultado originalmente viene en un object
    DynamicTableEntity dynaLaptop = 
    (
    DynamicTableEntity)retrievedLaptop.Result;

    Por ejemplo, llegó un embarque de 100 laptops de referencia X220 y hemos de agregarlos al inventario. Ya existirán? En ese caso debemos hacer un update. Si no, será una inserción nueva.

    Veamos; si la entidad no existe, entonces creamos una nueva:

    if(dynaLaptop==null)
    {
        dynaLaptop = new DynamicTableEntity()
        {
            PartitionKey = "Compute",
            RowKey = "Laptop"
        };
    }

    Solo las propiedades nativas de las tablas de Azure como PartitionKey, RowKey, ETag, y Timestamp vienen por defecto en una DynamicTableEntity. Las otras como “stockAmount” debemos incluirlas de la siguiente manera:

    dynaLaptop.Properties.Add("StockAmount", 
    EntityProperty.GeneratePropertyForInt(100)); dynaLaptop.Properties.Add("SubCategory",
    EntityProperty.GeneratePropertyForString("Laptop")); /*No es necesario crear propiedades para todos los campos
    existentes en la tabla. Aquí intencionalmente dejé por fuera
    ModelName y UnitPrice. Además dada la flexibilidad del
    Azure Storage, podemos agregar propiedades que no estaban
    antes en la tabla:*/
    dynaLaptop.Properties.Add("HDD",
    EntityProperty.GeneratePropertyForString("300GB"));

    Si por el contrario, la entidad ya existía, lo que hacemos es actualizarla:

    dynaLaptop["StockAmount"].Int32Value += 100;                
    //Aquí también podemos aregar nuevas propiedades
    dynaLaptop.Properties.Add("RAM", 
    EntityProperty.GeneratePropertyForString("32GB"));

    Así que ahora que ya tenemos la entidad lista para enviarla al storage usamos:

    TableOperation updateOperation = 
    TableOperation.InsertOrReplace(dynaLaptop); _tblInventory.Execute(updateOperation);

    Si luego de esto chequeamos la tabla en la nube tenemos:

    image

    Donde se aprecia que se insertó el nuevo elemento con la nueva propiedad HDD. Y que no pasó nada, por no haber indicado por ejemplo un ModelName.

    Ahora, si volvemos a ejecutar, lo que tendremos es un update, donde se sumarán otras 100 unidades y se agregará la propiedad RAM:

    image

    Y eso sería todo. La solución completa la pueden encontrar en GitHub:

    Hemos visto entonces cómo podemos acceder al Azure storage de una manera ágil y dinámica, sin necesidad de hacer montones de preparativos código circenses para lograrlo. Al mejor estilo nodejs ;) Algo muy útil cuando ya tenemos datos en la nube y queremos hacer un sencillo cliente para usarlos, sin necesidad de montar toda una arquitectura de mapeo detrás; cosa que obviamente aún sigue estando disponible para los desarrollos que lo justifiquen.

  • WarNov Developer Evangelist

    MÁS ALLA DE LOS MOBILE SERVICES: Una nueva era en el backend

    Update:

    Descarga la presentación PPT de este post de aquí:

    Y el código de ejemplo de aquí

    Seguramente ya has trabajado algo con Azure Mobile Services (en este artículo ZuMo) o al menos los has oído nombrar y sabes que son usados para que tus apps puedan acceder fácilmente a un backend a través de llamados http o de apis nativas en las plataformas de desarrollo de esas apps que lo encapsulan.

    Eso a simple vista suena muy sencillo y directo, pero en realidad nos estamos enfrentando a todo un nuevo paradigma de desarrollo que cambia las reglas y las formas en que se hacen las cosas, sobre todo para nosotros los desarrolladores que venimos del mundo .net tradicional.

    Con ZuMo podemos tratar escenarios tan abstraídos y sencillos como insertar datos en una tabla de SQL Database, reaccionar ante un update o enviar una push Notification:

    image

    Pero no estamos restringidos solo a estas operaciones básicas. Hoy en día a través de ZuMo podemos generar APIs personalizadas que podemos llamar cuando queramos. Podemos establecer Jobs que se ejecuten automática y periódicamente; podemos aprovechar los mecanismos de autenticación provistos out of the box y mantener el código y desplegarlo con GIT, entre muchas otras características provistas por esta plataforma que aprovecha lo mejor de Azure.

    En síntesis, hemos pasado de proveer un mecanismo que nos permitía guardar datos en la nube con cierta lógica, a tener todo un servidor de Backend para aplicaciones conectadas. Y obsérvese que no usé el término APPs, sino aplicaciones. Quiero decir entonces que esto no solo es para los móviles. Es tan robusto el esquema ofrecido por ZuMo, que claramente puede apoyar aplicaciones web, escritorio, etc. para ejecutar tareas específicas.

    De hecho, en ocasiones como lo veremos en este artículo, TODO el backend puede dejarse en manos de mobile services y así proveer funcionalidad a clientes web, móviles y lo que sea que pueda establecer una conexión HTTP. Y lo mejor de todo, es que esto se logra con ventajas sobre los modelos tradicionales.

    Qué ventajas se pueden obtener de un backend?

    1. Performance en la ejecución

    2. Performance en el storage

    3. Capacidad de storage

    4. Simplicidad de desarrollo

    5. Ahorro$

    Y en Azure qué elementos proveen estas ventajas?

    1. Servidores nodejs en Mobile Services
    image

    2. Azure Storage (tables, queues, blobs; particularmente: tables con su esquema NoSQL)

    Estos dos elementos tienen un performance sin igual comparado con esquemas tradicionales como los obtenidos con servidores ASP.NET o SQL Server. Adicionalmente el costo es mucho menor,

    Esto sucede ya que nodejs o el Azure storage no están enriquecidos con todas las abstracciones, características y herramientas adicionales que traen unos servidores tan robustos como IIS o SQL Server.

    Por ende, la ejecución es más veloz y los costos mucho menores!

    Son más bien elementos muy básicos que hacen extremadamente bien lo “poco” que hacen. Y esto nos lleva entonces a ver que tampoco es que sean la receta mágica de todo backend. Son más bien elementos que resuelven muy bien cierto tipo de problemas, al contrario de los servidores tradicionales que han sido construidos tratando que puedan solucionar todo tipo de problemas. Precisamente esto es lo que los convierte en piezas de software muy grandes que consumen muchos recursos y que pueden resultar más costosas.

    Esto no implica entonces que hoy en día no sigan siendo súper relevantes los servidores tradicionales (como IIS o SQL). De hecho imagínate construir todo un ERP encima de un modelo NoSQL donde no hay manejo de integridad referencial incluido en la herramienta. Eso es reinventar la rueda. Y ni hablar por ejemplo de la administración de conexiones y seguridad en los servicios que provee automáticamente IIS: en últimas tendrías que volver a escribir estos servidores para que hagan todo esto que ya se ha resuelto.

    El asunto es que hay tareas que no requieren de toda esta parafernalia. Por ejemplo una app que ofrezca resultados deportivos alrededor del mundo. Que se pueda ejecutar desde cualquier dispositivo o internet browser.

    Si lo pensamos, el modelo de datos es relativamente sencillo. Solo hay unas pocas entidades. Así que mapearlo en un esquema NoSQL, aunque es más complicado que mapearlo en un modelo E-R, no es una cosa imposible o muy difícil de hacer.

    Como es una aplicación abierta al público en general, el manejo de seguridad es bien sencillo. Además la funcionalidad la podemos mapear en sencillos métodos que ejecutan procesos sobre los datos almacenados. Estos métodos pueden ser escritos dentro de las apis personalizadas de mobile services y entonces ser llamados desde cualquier lugar del mundo a través de HTTP. Esto sin mencionar que ZuMo ofrece mecanismos de autenticación integrada con Facebook, Twitter, Google, Active Directory y la posibilidad de generar nuestros propios mecanismos.

    Obsérvese que al ser esta una aplicación mundial abierta al público, se espera una gran cantidad de peticiones por unidad de tiempo. Pero al estar todo montado en nodejs, podremos responderlas sin mucho problema y escalar de acuerdo a las poderosas ventajas ofrecidas por Microsoft Azure. El storage podrá almacenar inmensas cantidades de información (centenares de teras) y de acuerdo a cómo se hayan creado las tablas, ofrecerá unos tiempos de consulta inalcanzables por cualquier motor relacional de bases de datos (cosa que pasa por ejemplo con twitter o Facebook).

    Es este entonces un escenario propicio para implementar usando este nuevo paradigma de desarrollo en el cual se provee la lógica gruesa de la aplicación en un servidor nodejs para que los clientes (apps o browsers) puedan acceder a ella y el código que necesiten internamente sea mínimo. Facilitando por ende la engorrosa tarea de la portabilidad y esta es otra inmensa ventaja que sale naturalmente a través de este cambio.

    Con lo anterior solo bastaría hacer ciertas salvedades antes de dar el paso e ir a programar nuestro backend con nodejs en Mobile Services y Azure table storage.

    1. Mobile Services YA soporta .net como plataforma, a través de WebAPI. Esto permite que el desarrollador pueda hacer su backend sin necesidad de aprender nodejs. Pero como la arquitectura .net es distinta a la nodejs, entonces no se tendrán las ventajas de performance obtenidas con nodejs.

    2. Mobile Services SIEMPRE ha soportado SQL Database. Así que si se requiere generar un backend contra un modelo relacional tradicional (el desarrollo es más sencillo), no hay ningún problema.

    3. Desarrollar para nodejs y Azure table storage no es tan sencillo como desarrollar .net sobre un modelo relacional. Muchas abstracciones del modelo tradicional no se encuentran en nodejs ni el table storage. Así que en general habrá que esforzarse un poco más en el desarrollo y entender muy bien los conceptos de la arquitectura de un único hilo que no se bloquea en nodejs o de la forma en que las tablas nosql trabajan.

    4. No existe un IDE completo que abarque todo el ciclo de desarrollo con nodejs y Azure Storage. Por ejemplo con Visual Studio y ninguna otra herramienta adicional, es posible construir toda una aplicación cliente servidor desde la misma base de datos, pasando por la lógica de negocio, hasta el cliente web y las apps Windows. De hecho aunque el soporte a Javascript ha mejorado enormemente aún siguen habiendo algunas falencias:

    i. La habilidad de tener un tipo de proyecto que sea solo JavaScript: Esto se puede emular con un proyecto de tipo web, pero no es tan práctico

    ii. Incluir un ejecutor de scripts que use por ejemplo el intérprete de nodejs integrado: Emulable usando la Nugget Package Manager console para ejecutar los scripts.

    iii. No existe una integración visual con GIT para mantener y desplegar el código a Azure: Emulable usando la Nugget Package Manager console para ejecutar los comandos de GIT pero no en modo visual.

    iv. No existe un debugging difuso en tiempo de diseño para minimizar errores en un lenguaje interpretado como JavaScript: Update: El update 2 de Visual Studio 2013 ya incluye JSHint integrado en la edición de JavaScript, que permite tener más confianza y velocidad en el JavaScript que escribimos a través de su analizador de código estático:

    image

    5. No existe un cliente REST integrado para hacer testing rápido de los Servicios: Se puede usar una herramienta separada como Fiddler o Postman para hacer este testing, o tal vez usar curl desde la consola de NPM.

    Dadas todas estas condiciones recomiendo altamente usar un editor Javascript avanzado mientras existe más soporte en Visual Studio para lo que podríamos llamar proyectos Javascript o nodejs como tal. Una buena alternativa sería WebStorm, aunque no es gratuito, pero tiene versiones para Windows, Linux y Mac y posee todas las características descritas anteriormente.

    Adicionalmente para operar rápida y eficientemente el Azure Storage, recomiendo ampliamente Azure Management Studio de Cerebrata. Que a pesar de no ser gratuito ofrece numerosas ventajas para tener un buen entorno de desarrollo sobre el cloud en la parte del storage. Una alternativa gratuita podría ser Azure Storage Explorer.

    La metodología descrita hasta ahora, la he sumarizado luego de desarrollar varios proyectos sobretodo enfocados a aplicaciones móviles. Y para darle identidad, he creado el término nojats. Una aplicación nojats, está hosteada en Azure,  tiene su backend en nodejs (noj) y el almaceniamiento lo maneja con el Azure Table Storage (ats).

    Así que luego de esta introducción a las aplicaciones nojats, en posteriores posts, estaré mostrando más detalles de cómo optimizar el ambiente de desarrollo para explotar al máximo todas las ventajas que les he mencionado.

    Particularmente mostraré en la siguiente entrega cómo usar WebStorm para optimizar el trabajo con JavaScript Azure Mobile Services.

Page 1 of 1 (2 items)