CoreLib Storage Design Reference
Keyworks: Storage Azure Blob Table File
Introdução
O serviço de storage permite armazenar ficheiros e dados não estruturados em repositórios na cloud como o Azure Blob Storage ou o Azure Table Storage.
Esses serviços de storage são úteis para gerir determinados dados das aplicações, porque são mais económicos em determinados cenários de utilização e porque foram concebidos para esses cenários e para a integração com aplicações Web.
Conceitos
Azure Blob Storage
"Azure Blob storage is a service that stores unstructured data in the cloud as objects/blobs. Blob storage can store any type of text or binary data, such as a document, media file, or application installer. Blob storage is also referred to as object storage."
Azure Table Storage
"Azure Table storage is a service that stores structured NoSQL data in the cloud, providing a key/attribute store with a schemaless design. Because Table storage is schemaless, it's easy to adapt your data as the needs of your application evolve. Access to Table storage data is fast and cost-effective for many types of applications, and is typically lower in cost than traditional SQL for similar volumes of data."
Pressupostos
- Na cloud o armazenamento de ficheiros em base de dados torna-se uma má prática porque faz crescer as bases de dados em demasia e isso impacta os custos. Por essa razão é preferível armazenar os ficheiros em disco.
- As plataformas cloud disponibilizam serviços especificamente para este efeito que tornam possível a sua utilização abstraindo o sistema de ficheiros, através de uma API mais adaptada à construção de aplicações Web.
- Os repositórios de dados não estruturados (NoSql) são particularmente úteis para armazenar estruturas de dados que se espera que mudem de estrutura à medida que a respetiva aplicação evolui, simplificando os cenários de upgrade típicos em bases de dados SQL. É principalmente por essa razão que a sua utilização em aplicações Web tem vindo a aumentar.
Desenho
Storage Service Factory
Os vários serviços de storage disponilizados pela CoreLib podem ser acedidos a partir de uma
factory definida pelo tipo Primavera.Core.Storage.StorageServiceFactory (em Primavera.CoreLib.dll).
Atualmente estão disponíveis dois serviços:
- File Storage: desenhando especificamente para armazenamento de ficheiros.
- Table Storage: desenhado para armazenamento de dados em tabelas NoSql com uma estrutura chave/atríbuto.
A implementação concreta de cada um destes serviços é definida por configuração. As instâncias de cada serviço são disponibilizadas pela factory como nos exemplos seguintes:
IFileStorageService service = StorageServiceFactory.GetFileStorageService();
FileStorageServiceConfiguration configuration = FileStorageServiceConfiguration
.Default()
.WithImplementation<LocalFileStorageService>();
IFileStorageService service = StorageServiceFactory.GetFileStorageService(configuration);
ITableStorageService service = StorageServiceFactory.GetTableStorageService();
TableStorageServiceConfiguration configuration = TableStorageServiceConfiguration
.FromConfiguration()
.WithSetting(StorageSettingsBuilder.ServerConnectionTimeout, TimeSpan.FromSeconds(2))
.WithSetting(StorageSettingsBuilder.UseRootTracing, true);
ITableStorageService service = StorageServiceFactory.GetTableStorageService(configuration)
File Storage
O serviço IFileStorageService (Primavera.Core.Storage.Files.IFileStorageService em Primavera.CoreLib.dll)
permite armazenar ficheiros num respositório configurável (em disco ou usando Azure Blob Storage).
Arquitetura
O serviço permite gerir dois tipos de repositórios lógicos:
- Públicos: que armazenam ficheiros acessíveis publicamente.
- Privados: que armazenam ficheiros privados a determinado contexto (por exemplo, para determinada subscrição da aplicação).
Cada repositório é conhecido como um container que é identificado por um nome distinto. Também é possível criar o container para uma determinada conta e subscrição, sendo então gerado um nome único a partir desses dois identificadores.

O repositório concreto será então definido por configuração.
Na versão atual da CoreLib estão disponíveis 2 implementações do serviço:
Primavera.Core.Storage.Files.LocalFileStorageService- armazena os ficheiros em disco (local).Primavera.Core.Storage.Azure.Files.AzureFileStorageService- armazena os ficheiros na Azure Blob Storage.
API
A API permite aceder a um container público ou privado. Note-se que estes métodos devolvem uma referência para o container que pode não existir ainda fisicamente.
IFileStorageService service = StorageServiceFactory.GetFileStorageService();
IFileContainerReference publicContainer = service.GetPublicContainer("mycontainer");
IFileContainerReference privateContainer = service.GetPublicContainer("account", "subscription");
O interface Primavera.Core.Storage.Files.IFileContainerReference permite depois realizar todas
as operações sobre o container ou sobre os ficheiros nesse container:
Create()- cria o container (lança um erro se o container já existir).CreateIfNotExists()- cria o container se ainda não existir.Delete()- apaga o container (lança um erro se o container não existir).DeleteIfExists()- apaga o container se existir.Exists()- verifica se o container existe.UploadFile(fileName, stream)- faz o upload de um ficheiro para o container.DownloadFile(fileName)- faz o download do ficheiro indicado.FileExists(fileName)- verifica se o ficheiro existe.RenameFile(originalFileName, destinationFileName)- renomeia um ficheiro.DeleteFile(fileName)- elimina um ficheiro (lança um erro se o ficheiro não existir).DeleteFileIfExists(fileName)- elimina o ficheiro se este existir.GetFileUri(fileName)- devolve o URL para aceder ao ficheiro no container.
Esta API disponibiliza sempre a versão síncrona e a versão assíncrona de cada método (exemplo: Create e CreateAsync).
Configuração
A configuração do serviço pode ser feita no ficheiro de configuração da aplicação usando
o tipo Primavera.Core.Storage.Files.FileStorageServiceConfigurationSection e/ou em runtime
usando o tipo Primavera.Core.Storage.Files.FileStorageServiceConfiguration que disponibiliza
uma API fluente.
Exemplo de configuração no web.config:
<configSections>
<section name="fileStorageServiceConfiguration" type="Primavera.Core.Storage.Files.FileStorageServiceConfigurationSection, Primavera.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=33086db60a481256" />
</configSections>
<fileStorageServiceConfiguration>
<elements>
<add name="default">
<settings>
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=intdevunittesting;AccountKey=mLiNPWgCnVHFW74eFaC4XDiDki5LMPA9deKydHOsOhwx7xkgl0lwEYMcd/k6daJwSdTYpcTszE03lGXJ0ZGDrA==;EndpointSuffix=core.windows.net" />
<add key="ServerConnectionTimeout" value="00:00:04" />
<add key="RequireEncryption" value="false" />
<add key="MaximumExecutionTime" value="00:02:00" />
<add key="RetryPolicy" value="Linear" />
<add key="RetryPolicyMaximumAttempts" value="4" />
<add key="RetryPolicyBackoffTime" value="00:00:03" />
</settings>
</add>
</elements>
</fileStorageServiceConfiguration>
Exemplo de configuração em runtime:
FileStorageServiceConfiguration configuration = FileStorageServiceConfiguration
.Default()
.WithImplementation<LocalFileStorageService>();
IFileStorageService service = StorageServiceFactory.GetFileStorageService(configuration);
Implementação de um Provider
Pode-se implementar novos providers para suportar outros repositórios. Para o efeito bastará que a respetiva classe implemente o interface 'Primavera.Core.Storage.Files.IFileStorageService'.
Exemplo:
internal sealed partial class LocalFileStorageService : IFileStorageService
Table Storage
O serviço ITableStorageService (Primavera.Core.Storage.Tables.ITableStorageService em Primavera.CoreLib.dll)
permite armazenar dados em tabelas NoSql num respositório configurável (exemplo: usando Azure Table Storage).
Arquitetura
O serviço permite gerir uma tabela na storage.

O repositório concreto das tabelas será então definido por configuração.
Na versão atual da CoreLib estão disponíveis 2 implementações do serviço:
Primavera.Core.Storage.Tables.InMemoryTableStorageService- armazena as tabelas em memória (num dicionário).Primavera.Core.Storage.Azure.Tables.AzureTableStorageService- armazena os ficheiros na Azure Table Storage.
API
A API permite aceder a uma tabela. Note-se que estes métodos devolvem uma referência para a tabela que pode não existir ainda fisicamente.
ITableStorageService service = StorageServiceFactory.GetTableStorageService();
ITableReference table = service.GetTable("mytable");
O interface Primavera.Core.Storage.Tables.ITableReferenced permite depois realizar todas
as operações sobre a tabela ou sobre os dados nessa tabela:
Create()- cria a tabela (lança um erro se a tabela já existir).CreateIfNotExists()- cria tabela se ainda não existir.Delete()- apaga a tabela (lança um erro se a tabela não existir).DeleteIfExists()- apaga a tabela se existir.Exists()- verifica se a tabela existe.InsertRecord(record)- insere o registo especificado (lança um erro se já existir um registo com as mesmas chaves).InsertRecords(records)- insere todos os registos especificados (lança um erro se algum dos registos já existir).InsertOrReplaceRecord()- insere o registo especificado ou substitui-o se já existir.InsertOrReplaceRecords(record)- insere todos os registos especificados ou substitui os que já existirem.DeleteRecord(record)- elimina o registo especificado.ReplaceRecord(record)- substitui o registo especificado.RetrieveRecord(key1, key2)- devolve o registo que tem as chaves especificadas.TryRetrieveRecord(key1, key2, out result)- devolve o registo que tem as chaves especificadas.RetrieveRecords()- devolve todos os registos da tabela.
Esta API disponibiliza sempre a versão síncrona e a versão assíncrona de cada método (exemplo: Create e CreateAsync).
Configuração
A configuração do serviço pode ser feita no ficheiro de configuração da aplicação usando
o tipo Primavera.Core.Storage.Tables.TableStorageServiceConfigurationSection e/ou em runtime
usando o tipo Primavera.Core.Storage.Tables.TableStorageServiceConfiguration que disponibiliza
uma API fluente.
Exemplo de configuração no web.config:
<configSections>
<section name="tableStorageServiceConfiguration" type="Primavera.Core.Storage.Tables.TableStorageServiceConfigurationSection, Primavera.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=33086db60a481256" />
</configSections>
<tableStorageServiceConfiguration>
<elements>
<add name="release" type="Primavera.Core.Storage.Tables.InMemoryTableStorageService, Primavera.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=33086db60a481256" />
<add name="debug">
<settings>
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=intdevunittesting;AccountKey=mLiNPWgCnVHFW74eFaC4XDiDki5LMPA9deKydHOsOhwx7xkgl0lwEYMcd/k6daJwSdTYpcTszE03lGXJ0ZGDrA==;EndpointSuffix=core.windows.net" />
</settings>
</add>
</elements>
</tableStorageServiceConfiguration>
Exemplo de configuração em runtime:
TableStorageServiceConfiguration configuration = TableStorageServiceConfiguration
.FromConfiguration()
.WithSetting(StorageSettingsBuilder.ServerConnectionTimeout, TimeSpan.FromSeconds(2))
.WithSetting(StorageSettingsBuilder.UseRootTracing, true);
ITableStorageService service = StorageServiceFactory.GetTableStorageService(configuration)
Implementação de um Provider
Pode-se implementar novos providers para suportar outros repositórios. Para o efeito bastará que a respetiva classe implemente o interface 'Primavera.Core.Storage.Tables.ITableStorageService'.
Exemplo:
internal sealed partial class AzureTableStorageService : ITableStorageService
Questões em Aberto
- A API do serviço de file storage pode ser evoluída no futuro para disponibilizar comportamentos diferentes conforme o tipo de ficheiros armazenados.
FAQ
- P: A configuração do serviço de file storage é obrigatória?
- R: Não. Se não existir será utilizado o
AzureFileStorageService.
- R: Não. Se não existir será utilizado o
- P: A configuração do serviço de table storage é obrigatória?
- R: Também não. Se não existir será utilizado o
AzureTableStorageService.
- R: Também não. Se não existir será utilizado o