Siguiendo la serie de post relacionados con Windows Azure Storage, hoy me gustaría centrarme en las tablas, donde podemos almacenar datos de una forma estructurada pero no relacional. Este servicio es muy simple, altamente escalable y supone un coste menor que toda una base de datos relacional.
¿Qué no puedo hacer?
Debido a que no se tratan de tablas relacionales, no podremos:
- Crear foreign keys para las relaciones entre tablas.
- Realizar joins.
- Crear índices personalizados en las tablas.
¿Por dónde empiezo?
Antes de comenzar a programar, debemos conocer los tres pasos necesarios para crear nuestra tabla:
- Definir la entidad que almacenaremos en ella: Antes de crear una tabla necesitamos alguna forma de describir qué es lo que queremos almacenar.
- Definir el contexto: El contexto tratará las acciones que podremos realizar (creación, modificación, eliminación, etcétera), o lo que es lo mismo: Cómo queremos operar con ella.
- Creación de la tabla: Una vez que tenemos decidido el qué y el cómo estamos listos para su creación basándonos en los dos puntos anteriores.
Definir la entidad
La definición de la entidad se trata de una clase con sus correspondientes atributos. Para ser capaces de almacenar nuestra clase en una tabla de Windows Azure Storage necesitaremos además tres atributos adicionales que serán heredados de TableServiceEntity: PartitionKey, RowKey y Timestamp. Los dos primeros necesitamos inicializarlos en el momento de la creación de un nuevo objeto (el lugar ideal sería en el constructor) y el Timestamp será asignado de forma automática. Estas propiedades serán las que nos ayudarán a localizar un registro, y más en concreto PartitionKey será responsable de conseguir una escalabilidad eficiente si lo utilizamos de manera correcta.
Este podría ser un ejemplo de la definición de una entidad:
using System;
using Microsoft.WindowsAzure.StorageClient;namespace StorageManager_MvcWebRole.Models.Tables
{
public class Profile : TableServiceEntity
{public Profile()
{
PartitionKey = «Users»;
RowKey = Guid.NewGuid().ToString();
}public string UserName { get; set; }
public string Email { get; set; }
public int Age { get; set; }
public string BlogUrl { get; set; }
}
}
Definir el contexto
Una vez que tenemos el qué, podemos saber cómo vamos a manejarlo Creamos una nueva clase que herede de TableServiceContext. Si utilizamos Resharper nos advertirá antes de la compilación de que es necesario importar la librería System.Data.Services.Client.
De otro modo, si compilamos la solución, Visual Studio nos informará del mismo error:
Por otro lado, debemos implementar el constructor heredado de TableServiceContext ya que nos avisa de que no existe un constructor sin parámetros para la clase base y es necesario que la nuestra implemente uno. El motivo es que para crear el contexto de la tabla es necesario tener la dirección donde se aloja la misma, así como las credenciales para acceder.
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;namespace StorageManager_MvcWebRole.Models.Tables
{
public class ProfileTableServiceContext : TableServiceContext
{
private const string TableName = «Profiles»;public ProfileTableServiceContext(string baseAddress, StorageCredentials credentials)
: base(baseAddress, credentials) { }public IQueryable<Profile> Profiles
{
get { return CreateQuery<Profile>(TableName); }
}public void AddProfile(Profile newProfile)
{
AddObject(TableName, newProfile);
SaveChanges();
}public void RemoveProfile(string rowKey)
{
DeleteObject((from item in Profiles
where item.PartitionKey == «Users» && item.RowKey == rowKey
select item).First());
SaveChanges();
}public void UpdateProfile(Profile profileUpdated)
{
var profile = (from item in Profiles
where item.PartitionKey == «Users» && item.RowKey == profileUpdated.RowKey
select item).First();profile.UserName = profileUpdated.UserName;
profile.Age = profileUpdated.Age;
profile.Email = profileUpdated.Email;
profile.BlogUrl = profileUpdated.BlogUrl;UpdateObject(profile);
SaveChanges();
}}
}
Creación de la tabla
Por último ya podemos crear la tabla tanto en la nube como en local. Si bien es un paso que sólo debemos realizar una vez, podemos utilizar una aplicación externa como Cloud Storage Studio o en mi caso he utilizado la clase WebRole.cs que se genera cuando creamos un proyecto de tipo Windows Azure Project.
using System.Configuration;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using StorageManager_MvcWebRole.Models.Tables;namespace StorageManager_MvcWebRole
{
public class WebRole : RoleEntryPoint
{
public override bool OnStart()
{CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
configSetter(ConfigurationManager.ConnectionStrings[configName].ConnectionString));var storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
CloudTableClient.CreateTablesFromModel(typeof(ProfileTableServiceContext), storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);return base.OnStart();
}
}
}
Si quisiéramos conectar con una tabla en la nube, la cadena de conexión sería similar a la siguiente:
<Setting name=»StorageAccount» value=»DefaultEndpointsProtocol=http;AccountName=returngis;
AccountKey=KoJ9maUaV29LDdoajvkAIKzi98CH…..D1PJVpAUVOdb2ehHw5nN21XI2KF7Q==» />
Ya tenemos todos los ingredientes necesarios para trabajar con nuestra nueva tabla Lo único que faltaría sería usar el contexto para relacionarnos con la misma. Para realizar este ejemplo he creado un proyecto ASP.NET MVC y en pocas líneas hago uso de cada una de las acciones posibles:
using System;
using System.Linq;
using System.Web.Mvc;
using Microsoft.WindowsAzure;
using StorageManager_MvcWebRole.Models.Tables;namespace StorageManager_MvcWebRole.Controllers
{
public class TableController : Controller
{
private readonly CloudStorageAccount _account;
private readonly ProfileTableServiceContext _profileServiceContext;public TableController()
{
_account = CloudStorageAccount.DevelopmentStorageAccount;
_profileServiceContext = new ProfileTableServiceContext(_account.TableEndpoint.ToString(), _account.Credentials);
}public ActionResult Index()
{
return View(_profileServiceContext.Profiles);
}public ActionResult Create()
{
return View();
}[HttpPost]
public ActionResult Create(Profile newProfile)
{
_profileServiceContext.AddProfile(newProfile);return RedirectToAction(«Index»);
}public ActionResult Delete(string rowKey)
{
_profileServiceContext.RemoveProfile(rowKey);return RedirectToAction(«Index»);
}public ActionResult Edit(string rowKey)
{
return View((from profile in _profileServiceContext.Profiles
where profile.PartitionKey == «Users» && profile.RowKey == rowKey
select profile).First());}
[HttpPost]
public ActionResult Edit(Profile profileUpdated)
{
_profileServiceContext.UpdateProfile(profileUpdated);return RedirectToAction(«Index»);
}
}
}
Espero que sea de utilidad
¡Saludos!
Hola Gisela
Una de las cosas que Microsoft nos recalca de continuo es que Azure no es un sistema propietario, es decir, que puedes llevarte tus aplicaciones de Azure a un hosting de tu propiedad.
En este caso, si usamos estas entidades ¿existe soporte a ellas fuera de Azure?
Gracias
Hola Julio,
Buena pregunta 🙂 Windows Azure Storage como tal no existe fuera de la plataforma Azure.
En el caso de las tablas lo único que se me ocurre que se podría hacer sería migrarlas a una base de datos si cambiaras de hosting, pero a día de hoy no existe ninguna herramienta que te de esta opción. Tendrías que crearte una aplicación a medida para hacer la migración automática.
Un saludo.