Table Storage, Windows Azure

Continuando a série de posts sobre armazenamento no Windows Azure (Storage), vamos falar sobre Tables. Outra forma de persistência de dados existente no Windows Azure, que se baseia no armazenamento de objetos em grupos chamados de entidades.

1 – Windows Azure Tables

Uma coisa que é necessária deixar claro é que o Table Storage do Windows Azure trata-se de uma forma de persistência baseada em tabelas, porém, de forma não relacional como os bancos de dados tradicionais (Microsoft SQL Server, Oracle, e DB2) para este tipo de armazenamento o Azure disponibiliza o SQL Azure.

O Azure Table Storage é um serviço de armazenamento de dados não relacional que proporciona a persistência de entidades (estado dos objetos) dentro de tabelas que por sua vez ficam dentro de uma conta do Windows Azure. Estas tabelas funcionam como containers que armazenam um determinado tipo de entidade. Cada entidade contém suas devidas propriedades que funcionam como se fossem as colunas da tabela. Segue na Figura 1 uma imagem que ilustra a hierarquia do Table Storage.

Figura 1. Hierarquia Table Storage

Devido a sua natura não relacional, o Table Storage não é visto com muito bons olhos, principalmente por não conter uma forma de relacionar tabelas através de chave primaria e estrangeira.  Cada tabela não poderá exceder a 1MB e poderá conter no máximo 252 propriedades alem das três comentadas a seguir.

2 – Chave Primária

O fato do Table Storage não ser relacional não implica dizer que ele não contém uma chave primaria. Na verdade ele contém sim, mas em um conceito diferenciado de duas chaves que juntas compõem a identificação da entidade dentro da tabela. Essas chaves são definidas dentro de cada entidade através da herança da classe abstrata TableServiceEntity localizada no namespace Microsoft.WindowsAzure.StorageClient. Essa herança é que torna uma classe em uma entidade para ser persistida no Storage Services.

As propriedades herdadas são PartitionKey (string),RowKey (string) e TimeStamp (Datetime).  Sendo as duas primeiras a composição da identificação da entidade ou seja dentro da mesma tabela não poderá existir duas entidades com as duas propriedades iguais. Pode parecer estranho ao inicio para quem já está acostumado a trabalhar com dados relacionais, mas aqui caberá a arte do desenvolvedor criar uma forma de persistir os dados utilizando a composição de chaves ao seu favor.

  • PartitionKey: Primeira parte da Primary Key da entidade. Esta propriedade é de preenchimento obrigatório, e além de fazer parte da identificação ela também gerencia o particionamento dos dados dentro no balanceamento de carga dos servidores de armazenamento. Por exemplo:
    Digamos que sua empresa tenha uma tabela de produtos que utiliza o PartitionKey para diferenciar o tipo do produto. Sabe-se que no inicio da veiculação de uma propaganda sobre uma determinada oferta, a quantidade de consultas a um determinado tipo de produto aumenta. Através do PartitionKey é possível distribuir somente estes dados para outros nós do cluster de servidores para que através do balanceamento de carga não aconteça um sobrecarga de consultas em um único storage.
  • RowKey: Segunda parte da Primary Key, de preenchimento obrigatório.

Iniciando nosso exemplo prático, vamos definir uma entidade para cadastrar visitantes. Como já foi citado anteriormente, deverá ser importado para a classe o namespace Microsoft.WindowsAzure.StorageClient e herdada à classe abstrata TableServiceEntity, segue exemplo na Listagem 1.


using Microsoft.WindowsAzure.StorageClient;

namespace MyFirstAzure.Storage.Tabelas.Entidades
{
public class Visitante:TableServiceEntity
{

/// <summary>
/// Método Construtor Default
/// Se faz necessário para poder criar os objetos no retorno das consultas
/// </summary>
 public Visitante() { }

/// <summary>
/// Método Constrtutor para a criação de objetos a serem persistidos
/// </summary>
/// <param name="partitionKey">Chave de Particionamento</param>
/// <param name="rowKey">Identificador de Linha</param>
 public Visitante(string partitionKey, string rowKey): base(partitionKey, rowKey){ }

public string Nome { get; set; }

public string Cidade { get; set; }

public int Idade { get; set; }

public string Comentario { get; set; }
 }
}

Listagem 1. Definição de Entidaes

Vejam ainda na Listagem 1 que temos dois métodos construtores. O Default é obrigatório e sem ele não é possível retorno os objetos de através das consultas (caso tente ser feito, será mostrado um erro em tempo de execução). O Construtor com dois parâmetros serve para preencher os dados referentes à chave primaria da entidade.

3 – Gerenciando as Tabelas, Containers para as entidades

Como já foi dito, o Table Storage se divide em um container que armazena as Tabelas, este por sua vez armazenas as entidades. O controle das tabelas é feito através da classe nativa do Windows Azure, CloudTableClient. A Instanciação desta classe requer que seja informado a URL do Table Storage e das credenciais de acesso. No artigo passado (Blobs) existe uma demonstração de uma classe armazena estes dados e retorna-os em métodos estáticos (StorageControle).Segue na Listagem 2, o Código necessário para criar uma nova tabela.


public void criaTabela(string nome)
{
CloudTableClient myCloudTable = new CloudTableClient(StorageControle.TableURL, StorageControle.Credenciais);
myCloudTable.CreateTableIfNotExist(nome);
}

Listagem 2. CloudTableClient para gerenciar as tabelas

4 – Contexto, Persistindo os Dados

Depois de criada as tabelas, temos de persistir os dados (entidades) no banco de dados. Essa persistência é realizada através da herança da classe abstrata TableServiceContext. Está classe não contem nenhuma operação, pois todas são feitas na classe base.  Segue Código da classe na Listagem 3.

 

using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace MyFirstAzure.Storage.Tabelas
{
internal class TabelaContexto : TableServiceContext
{
public TabelaContexto(string path, StorageCredentials credenciais) : base(path, credenciais) { }
}

Listagem 3. Classe de Persistência dos dados

Criada a classe de persistência, vamos agora criar uma classe que irá operá-la. Essa classe irá receber as entidades a serem persistidas através de Generics e irá salva-los na base.  O método salvarTabela recebe a entidade a ser salva através dos parâmetros do método.

O método de  SalvarTabela é bem simples, ele primeiro certifica-se  que a tabela já existe (Aqui estou utilizando o nome da entidade para armazenar como tabela através do Type). Depois é chamado o AddObject da classe contexto passando como parâmetros o nome da tabela(criado na linha anterior) e a entidade a ser persistida e por fim ele salva todas as alterações no banco através do comando SaveChanges(). Segue  código na Listagem 4.

using System;
using System.Collections.Generic;
using System.Linq;using Microsoft.WindowsAzure.StorageClient;
namespace MyFirstAzure.Storage.Tabelas
{
public class TabelasControle<T> where T:TableServiceEntity
{
TabelaContexto myTabelaContexto = null;
CloudTableClient myCloudTable = null;
public TabelasControle()
{
myTabelaContexto = new TabelaContexto(StorageControle.TableURL, StorageControle.Credenciais);
myCloudTable = new CloudTableClient(StorageControle.TableURL, StorageControle.Credenciais);
}
public void criaTabela(string nome)
{
CloudTableClient myCloudTable = new CloudTableClient(StorageControle.TableURL, StorageControle.Credenciais);
myCloudTable.CreateTableIfNotExist(nome);
}
public void salvarTabela(TableServiceEntity entidade)
{
try
{
this.criaTabela(entidade.GetType().Name);
//Adiciona o objeto a tabela criada
myTabelaContexto.AddObject(entidade.GetType().Name, entidade);
//Salva as alterações
myTabelaContexto.SaveChanges();
}
catch (Exception ex) { throw ex;}}}}

Listagem 4. Classe de Controle de Persistência das Tabelas

Vamos também adicionar um método a nossa classe de Controle que dado uma tabela e seu PartitionKey, ele irá retornar a quantidade de registros salvos. Notem que a consulta é feita utilizando LINQ to Entities através do método CreateQuery. Segue código na Listagem 5.


public int qtdRegistros(string nomeTabela, string partitionKey)
{
this.criaTabela(nomeTabela);

var query = from c in myTabelaContexto.CreateQuery<T>(nomeTabela)
where c.PartitionKey.Equals(partitionKey)
select c;
return query.ToList().Count;
}

Listagem 5. Código que dado um PartitionKey, retorna a quantidade de registros salvos

Para terminamos a implementação da nossa classe de controle, vamos criar mais dois métodos responsáveis por retornar os dados armazenados. Os métodos terão o mesmo nome (listar) diferenciando apenas as suas assinaturas. A estrutura dos dois é similar: Utilizando LINQ consultamos o banco de dados através da instanciação da classe contexto e utilizando o método CreateQuery e retornamos uma lista dos dados obtidos. Segue implementação na Listagem 6.

 

public List<T> listar(string nomeTabela)
{
     var query = from c in myTabelaContexto.CreateQuery<T>(nomeTabela)
                 select c;
     return query.ToList();
}

public List<T> listar(string nomeTabela, string partitionKey)
{
     var query = from c in myTabelaContexto.CreateQuery<T>(nomeTabela)
                 where c.PartitionKey.Equals(partitionKey)
                 select c;
     return query.ToList();
}

Listagem 6. Métodos de Consulta da classe Controle


Para facilitar o entendimento, segue abaixo na Figura 2 o diagrama de classe da aplicação. As classes recolhidas são nativas as bibliotecas do Windows Azure.

Figura 2. Diagrama de Classe das tabelas que utilizam o Table Service

5 – Salvando e Retornado os dados

Para finalizar, vamos salvar os dados utilizando a estrutura que foi montada neste artigo. Aqui estou abstraindo um pouco as boas práticas de programação para torna o entendimento um pouco mais fácil.

Para salvar uma entidade precisamos inicialmente instanciar a classe que à representa preenchendo os métodos construtores com os dados que irão compor a chave primária, aqui utilizamos o PartitionKey para armazenar o nome da pessoa e o RowKey para armazenar um número seqüencial, assim podemos facilmente capturar os dados de somente um visitante.

A classe de controle deverá ser iniciada informando o tipo, segue exemplo na Listagem 7.


TabelasControle<Visitante> tcVisitante = new TabelasControle<Visitante>();
Visitante visitante = new Visitante(txtNome.Text, (tcVisitante.qtdRegistros("Visitante", txtNome.Text)+1).ToString());
visitante.Nome = "Olavo Neto";
visitante.Idade = 25;
visitante.Cidade = "Fortaleza";
visitante.Comentario = "Este é um teste";
 tcVisitante.salvarTabela(visitante);

Listagem 7. Persistindo dados

Para retorna as entidades também deverá ser utilizada a classe de controle, informando o nome da tabela para retornar todos os dados ou informando o PartitionKey para realizar um filtro nos dados salvos.

—–

Bom pessoal é isto, apesar de um pouco longo, espero ter demonstrado como é fácil trabalhar com Table Storage para Windows Azure e ter demonstrado que apesar de certas diferenças entre as plataformas (encaradas como limitações) trabalhar com persistência de entidades na plataforma de computação das nuvens da Microsoft é bastante simples.

Quem quiser ver como ficou o exemplo implementado, temporariamente o link http://8667ebce1752445bb9ddaf86ff7e4b68.cloudapp.net/Visitantes.aspx da minha conta do Azure ainda está funcionando.

Anúncios
Esse post foi publicado em Windows Azure. Bookmark o link permanente.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s