Las tablas de Windows Azure Storage

En este post, voy a centrarme en las tablas de Windows Azure Storage, las cuales podemos definirlas como un conjunto de datos estructurados pero no relacionales. Es decir, tenemos un conjunto de registros con sus correspondientes columnas y primary keys pero no es posible la relación entre una tabla y otra del Storage. En el caso de necesitar tablas relacionales, tenemos SQL Azure.

Para comenzar, vamos a crear una pequeña aplicación donde almacenamos perfiles de usuario. Abrimos Visual Studio como administrador y creamos un nuevo proyecto de tipo Cloud Service con un Web Role.

Una vez creado, nos aparecerán dos proyectos: El web role y el proyecto de configuración de Windows Azure.

CREACIÓN DE LOS DATOS ESTRUCTURADOS

Como he mencionado antes, el contenido de las tablas de Storage son datos estructurados. Cada registro de esa tabla será una entidad y el conjunto de registros será una colección de entidades. En este caso, nuestra entidad se llamará Profile y estará compuesta por un nombre y un email. Para representar esta entidad/registro de nuestra tabla, creamos la siguiente clase en el proyecto ProfileManager:

using System;

namespace ProfileManager
{
public class Profile : Microsoft.WindowsAzure.StorageClient.TableServiceEntity
{
public Profile()
{
//PartitionKey y RowKey son requeridas para cada entidad añadida a la tabla
PartitionKey = "Users";
RowKey = string.Format("{0:10}_{1}", DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid());
}

public string UserName { get; set; }
public string Email { get; set; }

}
}

Lo más importante a tener en cuenta en esta clase es que hereda de TableServiceEntity, que está dentro de una de las librerías de Windows Azure.
Hemos definido un constructor, donde inicializamos dos propiedades heredadas, PartitionKey y RowKey, las cuales son requeridas para poder insertar, modificar y eliminar correctamente una entidad en la tabla. De lo contrario, generaría una excepción.

Las propiedades heredadas por cada entidad son:

  • PartitionKey, se utiliza para determinar en qué partición debe estar la entidad que se está creando. Esto es debido a que las tablas están particionadas para soportar el balanceo de carga, a través de nodos de almacenamiento. Las entidades que pertenecen a una misma PartitionKey permanecen juntas. Además, esta clave forma parte de la primary key del registro. Para mejorar la escalabilidad de la aplicación, debemos tener en cuenta que se considera recomendable clasificar nuestros datos entre varias Partition Keys.
  • RowId, se trata de la segunda parte de la primary key. Es un identificador único dentro de la partición.
  • Timespan, es una propiedad de tipo DateTime que se utiliza para guardar la fecha de modificación de la entidad.

 

CONTEXTO DE LA TABLA

Por otro lado, necesitamos generar una serie de operaciones para que podamos trabajar con la tabla que, en un futuro, estará ubicada en la nube. Para ello, creamos una clase que herede de TableServiceContext, donde agregaremos las opciones de recuperar y añadir entidades en la tabla Profiles.

using System.Linq;
using Microsoft.WindowsAzure;

namespace ProfileManager
{
public class ProfileTableServiceContext : Microsoft.WindowsAzure.StorageClient.TableServiceContext
{
public ProfileTableServiceContext(string baseAddress, StorageCredentials credentials) : base(baseAddress, credentials) { }

public IQueryable<Profile> Profiles
{
get { return CreateQuery<Profile>("Profiles"); }
}

public void AddProfile(string userName, string email)
{
AddObject("Profiles", new Profile { UserName = userName, Email = email });
SaveChanges();
}
}
}

Lo primero que observamos es que recupera el constructor de la clase base. Es un paso obligatorio ya que la misma no contiene un constructor sin parámetros, lo cual es lógico, debido a que es necesario una dirección que nos indique dónde está el endpoint donde queremos operar y, como segundo parámetro, las credenciales para tener acceso.

Nota: Es necesario añadir la dll System.Data.Services.Client.

CADENA DE CONEXIÓN A DESARROLLO

Antes de realizar las pruebas oportunas contra la nube, podemos realizar un testeo inicial con el entorno de desarrollo Development Storage y Development Fabric. Para realizar las pruebas en local, creamos una entrada en la sección connectionStrings del archivo de configuración con el siguiente valor:

<connectionStrings>
<add name="AzureConnection" connectionString="UseDevelopmentStorage=true"/>
</connectionStrings>

CREACIÓN DE LA TABLA

Tanto en local como en la nube, antes de añadir registros, necesitamos crear la tabla en el Storage. Si bien es un paso que solamente debemos realizar una vez, podemos utilizar el archivo de inicio WebRole.cs que se generó cuando creamos el proyecto. Dentro del método OnStart(), añadimos el siguiente código:

using System.Configuration;
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;

namespace ProfileManager
{
public class WebRole : RoleEntryPoint
{
public override bool OnStart()
{
DiagnosticMonitor.Start("DiagnosticsConnectionString");

// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += RoleEnvironmentChanging;

CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
configSetter(ConfigurationManager.ConnectionStrings[configName].ConnectionString));

var storageAccount = CloudStorageAccount.FromConfigurationSetting("AzureConnection");
CloudTableClient.CreateTablesFromModel(typeof(ProfileTableServiceContext),
storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
return base.OnStart();
}

Lo primero que debemos especificar es de qué manera podemos recuperar la configuración necesaria para conectar a la cuenta de Storage a través de SetConfigurationSettingPublisher.
Una vez indicado el modo, recuperamos la cadena de conexión utilizando FromConfigurationSetting y, por último, creamos la tabla llamado a CreateTablesFromModel, pasándole como parámetros la clase que creamos con las operaciones que podemos realizar contra nuestra tabla, y que estaba heredando de TableServiceContext, el endpoint y las credenciales.

Para probar el ejemplo, he creado un pequeño formulario con dos textbox donde añadimos un nombre y un email y, pulsando en el botón Add Profile, añadimos esos datos en forma de entidad a la tabla del Storage y mostramos las entidades almacenadas hasta el momento.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ProfileManager.Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<div>
<asp:Label runat="server" ID="lblUserName" Text="UserName:"></asp:Label>
<asp:TextBox runat="server" ID="txtUserName"></asp:TextBox>
<asp:Label runat="server" ID="lblEmail" Text="Email:"></asp:Label>
<asp:TextBox runat="server" ID="txtEmail"></asp:TextBox>
<asp:Button runat="server" ID="btnAdd" Text="Add Profile" OnClick="btnAdd_Click" />
</div>
<div>
<asp:GridView ID="grvProfiles" runat="server" BackColor="White" BorderColor="#E7E7FF"
BorderStyle="None" BorderWidth="1px" CellPadding="3" GridLines="Horizontal">
<RowStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" />
<FooterStyle BackColor="#B5C7DE" ForeColor="#4A3C8C" />
<PagerStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" HorizontalAlign="Right" />
<SelectedRowStyle BackColor="#738A9C" Font-Bold="True" ForeColor="#F7F7F7" />
<HeaderStyle BackColor="#4A3C8C" Font-Bold="True" ForeColor="#F7F7F7" />
<AlternatingRowStyle BackColor="#F7F7F7" />
</asp:GridView>
</div>
</div>
</form>
</body>
</html>

using System;
using Microsoft.WindowsAzure;

namespace ProfileManager
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}

protected void btnAdd_Click(object sender, EventArgs e)
{
var storage = CloudStorageAccount.FromConfigurationSetting("AzureConnection");
var operations = new ProfileTableServiceContext(storage.TableEndpoint.ToString(), storage.Credentials);

operations.AddProfile(txtUserName.Text, txtEmail.Text);
grvProfiles.DataSource = operations.Profiles;
grvProfiles.DataBind();
}
}
}

 


CADENA DE CONEXIÓN PARA WINDOWS AZURE STORAGE

Cuando ya estemos listos para utilizar el Storage de Azure, necesitamos crear una cuenta de almacenamiento y  cambiar la cadena de conexión:

<connectionStrings>
<add name="AzureConnection" connectionString="DefaultEndpointsProtocol=https;AccountName=YOUR_ACCOUNTNAME;AccountKey=PRIMARY_ACCESS_KEY"/>
</connectionStrings>

Nota: Existen una serie de combinaciones para realizar la conexión pero, por el momento, usaremos la mencionada a modo introductorio.

Si arrancamos de nuevo la aplicación y añadimos un par de registros, obtendríamos el siguiente resultado.

CLOUD STORAGE STUDIO

Para finalizar me gustaría mencionar la siguiente aplicación, la cual es bastante útil para visualizar, crear, modificar y eliminar elementos del Storage de Azure (tanto tablas, como blobs y queues). Su nombre es Cloud Storage Studio y, si bien es de pago, tenemos la posibilidad de probarla de forma gratuita durante 30 días.
Si accedemos a la tabla que acabamos de generar con esta demo, podemos ver todos los detalle de la misma tal y como se muestra en la siguiente captura.

¡Saludos!

Un comentario en “Las tablas de Windows Azure Storage”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *