Таблицы
Сервис таблиц в Windows Azure не является реляционным.
Нельзя-
1) Создавать внешние ключи между таблицами.
2) Выполнять операцию объединения на стороне сервера
3) Создавать произвольные индексы
4) Выполнять такие функции, как, например, Count(), на стороне сервера
Применение сервиса таблиц актуально, если вам не требуется реляционное хранилище или если вам не нужны операции объединения (joins) на стороне сервера, а также в тех случаях, когда набор данных не очень велик и операции объединения можно обрабатывать на стороне клиента с помощью LINQ. Кроме этого, стоит оценить выгоды использования сервиса таблиц хранилища Windows Azure в том случае, если данных у вас больше, чем максимальный объем, поддерживаемый SQL Azure (который в настоящее время составляет 150 Гб). Заметьте
Таблицы Windows Azure хранят данные в виде коллекций сущностей. Сущности аналогичны записям. Сущность имеет первичный ключ и набор свойств. Свойства являются парами ключ-значения, что аналогично столбцам.
Для доступа к сервису таблиц вы можете использовать REST API, совместимый с WCF Data Services (ранее ADO.NET Data Services Framework). Для чтения и записи данных в сервис таблиц в упражнении используется WCF Data Services Client Library (ранее .NET Client Library). Необходимо учитывать то, что между локальным эмулятором сервиса хранилища таблиц и облачным хранилищем таблиц имеются определенные различия. К ним относятся:
Запись в таблицах состоит из следующих компонентов и концепций:
Пример: POCO, наследуемый от TableServiceEntity
public class MyClass : TableServiceEntity { public MyClass() { base.PartitionKey = "Clients"; base.RowKey = Guid.NewGuid().ToString(); } public MyClass(string PartitionKey, string RowKey) { base.PartitionKey = PartitionKey; base.RowKey = RowKey; } public string FName { get; set; } public string LName { get; set; } }
Пример: сокращенная запись
public MyClass():base("Clients", Guid.NewGuid().ToString()) { } public MyClass(string PartitionKey, string RowKey):base(PartitionKey,RowKey) { }
Класс TableServiceEntity является частью библиотеки Microsoft.WindowsAzure.StorageClient. Он определяет системные свойства PartititionKey, RowKey и TimeStamp, необходимые для каждой сущности в таблице Windows Azure. данном случае мы определяем для свойства PartitionKey фиксированное значение. В реальной ситуации лучше выбирать значение, обеспечивающее балансировку нагрузки между узлами хранилища, динамическое вычисляемое значение, например, день добавления сущности и так далее.
Ссылка на таблицу выглядит стандартно для именования сущностей в сервисах хранилища Windows Azure:
http://<account>.table.core.windows.net/<TableName>
Рекомендация: таблицы данных должны создаваться только один раз. Обычно этот процесс выполняется на стадии подготовки и редко – в коде приложения. Application_Start в классе Global является рекомендуемым местом для помещения логики инициализации.
void Application_Start(object sender, EventArgs e) { CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSettingPublisher) => { var connectionString = RoleEnvironment. GetConfigurationSettingValue(configName); configSettingPublisher(connectionString); }); //получение данных из настроек аккаунта хранилища, в данном случае это строка подключения DataStoragevar account = CloudStorageAccount.FromConfigurationSetting("DataStorage");//создание таблиц согласно контексту данных CloudTableClient.CreateTablesFromModel(typeof(MyClassDataContext), account.TableEndpoint.AbsoluteUri, account.Credentials); }
void Application_Start(object sender, EventArgs e) { CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSettingPublisher) => { var connectionString = RoleEnvironment. GetConfigurationSettingValue(configName); configSettingPublisher(connectionString); }); //получение данных из настроек аккаунта хранилища, в данном случае это строка подключения DataStorage
var account = CloudStorageAccount.FromConfigurationSetting("DataStorage");
//создание таблиц согласно контексту данных CloudTableClient.CreateTablesFromModel(typeof(MyClassDataContext), account.TableEndpoint.AbsoluteUri, account.Credentials); }
Для удобства и аккуратности программирования необходимо выполнять все операции над определенными таблицами из соответствующих классов контекстов, которые наследуются от класса DataServiceContext.
using System.Linq; using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.StorageClient; namespace WebRole { public class MyClassDataContext : TableServiceContext { public MyClassDataContext(string baseAddress, StorageCredentials credentials) : base(baseAddress, credentials) { } public IQueryable<MyClass> units { get { return this.CreateQuery<MyClass>("units"); } } } }
Для использования и манипуляций над содержимым таблицы необходимо создать объект типа CloudTableClient, предоставляющий следующую функциональность:
CreateTable – создание таблицы с определенным именем. В случае наличия таблицы с таким именем будет выброшено исключение StorageClientException. Наиболее предпочитаемым способом создания таблиц является их создание с помощью класса контекста.
CloudTableClient.CreateTablesFromModel(typeof(MyClassDataContext), account.TableEndpoint.AbsoluteUri, account.Credentials); }
CreateTableIfNotExist – создание таблицы с определенным именем только в том случае если ее не существует.
DoesTableExist – проверка на существование таблицы с определенным именем.
DeleteTable – удаление таблицы и ее содержимого из хранилища. если этой таблицы не существует, будет выброшено исключение StorageClientException.
DeleteTableIfExist – удаление таблицы и содержимого из хранилища только в том случае если она существует.
public static void DeleteTable() { var account = CloudStorageAccount.FromConfigurationSetting("DataStorage"); account.CreateCloudTableClient().DeleteTableIfExist("units"); }
ListTables – получение списка всех таблиц. Возможно указание префикса для фильтрации имен таблиц.
public static void GetListTables() { var account = CloudStorageAccount.FromConfigurationSetting("DataStorage"); IEnumerable<string> tables = account.CreateCloudTableClient().ListTables("mytableprefix"); foreach (var table in tables) { System.Diagnostics.Trace.WriteLine(table); } }
AddObject – добавление сущности в таблицу.
public static void AddEntityToTable(){ var account = CloudStorageAccount.FromConfigurationSetting("DataStorage"); var tableClient = account.CreateCloudTableClient(); TableServiceContext ctx = tableClient.GetDataServiceContext(); MyClass unit = new MyClass("Alex", "Belotserkovskiy"); unit.FName = “FName”;unit.LName = “LName”; ctx.AddObject("mytable", customer1); serviceContext.SaveChangesWithRetries();}
public static void AddEntityToTable(){
var account = CloudStorageAccount.FromConfigurationSetting("DataStorage"); var tableClient = account.CreateCloudTableClient(); TableServiceContext ctx = tableClient.GetDataServiceContext(); MyClass unit = new MyClass("Alex", "Belotserkovskiy"); unit.FName = “FName”;
unit.LName = “LName”;
ctx.AddObject("mytable", customer1); serviceContext.SaveChangesWithRetries();
}
В том случае, если у вас происходит создание множества сущностей, дешевле (с позиции количества транзакций) и быстрее будет выполнить пакет запросов. Для этого всего лишь надо передать в SaveChangesWithRetries аргумент. Пакеты запросов характеризуются:
ctx.SaveChangesWithRetries(SaveChangesOptions.Batch);
Получение объектов может быть реализовано с помощью LINQ-утверждения. Для возвращения всех записей в пределах одной партиции и имеющих определенное значение поля, можно воспользоваться следующим LINQ-утверждением.
CloudTableQuery<MyClass> qry = (from e in ctx.CreateQuery<MyClass>("mytable") where e.PartitionKey == "Belotserkovskiy" && e.FName == “Alex” select e).AsTableServiceQuery<MyClass>(); foreach (MyClass unit in qry) { Console.WriteLine("{0}, {1}", unit.PartitionKey, unit.FName); }
LINQ-утверждения можно также использовать и для любых других операций, в том числе обновления и удаления сущностей.
MyClass unit = (from e in ctx.CreateQuery<MyClass>("mytable") where e.PartitionKey == "Belotserkovskiy" && e.FName == “Alex” select e).AsTableServiceQuery<MyClass>().FirstOrDefault();unit.FName= "Mike"; ctx.UpdateObject(unit); ctx.SaveChangesWithRetries(); ctx.DeleteObject(unit); ctx.SaveChangesWithRetries();
MyClass unit = (from e in ctx.CreateQuery<MyClass>("mytable") where e.PartitionKey == "Belotserkovskiy" && e.FName == “Alex” select e).AsTableServiceQuery<MyClass>().FirstOrDefault();
unit.FName= "Mike"; ctx.UpdateObject(unit); ctx.SaveChangesWithRetries();
ctx.DeleteObject(unit);
ctx.SaveChangesWithRetries();
Alexander Belotserkovskiy edited Revision 1. Comment: added info about differences between emulator and cloud table storage
Fernando Lugão Veltem edited Original. Comment: alter title and tags. added ru-ru