|
Вчера нашел ссылку на Windows Azure Training Kit и решил сделать несколько лаб по SQL Data Services (старое название включало слово Server, но от него решили избавиться). Первое, что нужно это возможность создать себе учетную запись. Такая у меня уже имелась. Иначе прийдется доставать код инициации. Одной учетной записи достаточно для доступа ко всем элементам Windows Azure. REST интерфес Дальше раскрываем первую лабу и делаем создаем себе Authority (можно считать, что это что-то среднее между базой данных и экземпляром SQL Server в облаке). Для того чтобы, что-то сделать с SDS нужно послать HTTP запрос с XML содержимым говорящим, что делать. Для создания Authority по адресу https://data.database.windows.net/v1/ посылаем запрос: 1: <Authority xmlns="http://schemas.microsoft.com/sitka/2008/03/">
2: <Id>kosinskylabs</Id>
3: </Authority>
В результате мой Authority называется: https://kosinskylabs.data.database.windows.net/v1/. Если зайти по этому адресу с указанием логина и пароля, то получим что-то похожее на:
1: - <s:Authority xmlns:s="http://schemas.microsoft.com/sitka/2008/03/"
2: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3: xmlns:x="http://www.w3.org/2001/XMLSchema">
4: <s:Id>kosinskylabs</s:Id>
5: <s:Version>69802</s:Version>
6: </s:Authority>
Здесь я наступил на первые грабли. Названием должно состоять только из маленьких латинских букв и цифры (об этом честно сообщают в Note к лабе, но кто ж такое читает :) ). И при создании не корректного мы получим ошибку HTTP (400) Bad Request, хотя в теле ответа будет реальное описание ошибки:
1: 400: The authority id provided results in a invalid hostname and is therefore invalid.
2: Names can only be made up of lowercase letters 'a'-'z', the numbers 0-9and the hyphen.
3: The hyphen may not lead or trail in the name.
4: <s:Error xmlns:s="http://schemas.microsoft.com/sitka/2008/03/">
5: <s:Code>6002</s:Code>
6: <s:Message>The authority id provided results in a invalid hostname and is therefore invalid.
7: Names can only be made up of lowercase letters 'a'-'z', the numbers 0-9 and the hyphen.
8: The hyphen may not lead or trail in the name.</s:Message>
9: </s:Error>
Дальше создаем контейнер. Контейнер будет хранилищем наших сущностей. Для этого посылаем на адрес нашего Authority (https://kosinskylabs.data.database.windows.net/v1/) POST запрос:
1: <Container xmlns="http://schemas.microsoft.com/sitka/2008/03/">
2: <Id>tasks</Id>
3: </Container>
Контейнер будет иметь адрес: https://kosinskylabs.data.database.windows.net/v1/tasks
Дальше создаем объекты. Их называют Entity. В отличии от таблиц базы данных свойства у них могут быть произвольные. Например, для создания задачи отсылаем на адрес нашего контейнера запрос:
1: <Task xmlns:s="http://schemas.microsoft.com/sitka/2008/03/"
2: xmlns:x="http://www.w3.org/2001/XMLSchema"
3: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4: <s:Id>task_b</s:Id>
5: <Subject xsi:type="x:string">Task B</Subject>
6: <StartDate xsi:type="x:dateTime">2008-11-02 12:20:51Z</StartDate>
7: <DueDate xsi:type="x:dateTime">2008-11-12 12:20:51Z</DueDate>
8: <Status xsi:type="x:string">In Progress</Status>
9: <Priority xsi:type="x:string">Normal</Priority>
10: <IsComplete xsi:type="x:boolean">false</IsComplete>
11: <PercentComplete xsi:type="x:decimal">0</PercentComplete>
12: </Task>
Все кроме Id не обязательно.
Чтобы вытащить данные мы должны используя GET, что-то похожее на LINQ. Например, чтобы выбрать все элементы контейнера делаем запрос:
1: from e in entities select e
Если запрашиваем: https://kosinskylabs.data.database.windows.net/v1/tasks?q=from e in entities select e, то в результате получим:
1: - <s:EntitySet xmlns:s="http://schemas.microsoft.com/sitka/2008/03/"
2: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3: xmlns:x="http://www.w3.org/2001/XMLSchema">
4: - <Task>
5: <s:Id>task_a</s:Id>
6: <s:Version>59588</s:Version>
7: <Subject xsi:type="x:string">Task A</Subject>
8: <StartDate xsi:type="x:dateTime">2008-10-18T11:20:51</StartDate>
9: <DueDate xsi:type="x:dateTime">2008-10-28T12:20:51</DueDate>
10: <Status xsi:type="x:string">Finished</Status>
11: <Priority xsi:type="x:string">Normal</Priority>
12: <IsComplete xsi:type="x:boolean">false</IsComplete>
13: <PercentComplete xsi:type="x:decimal">0</PercentComplete>
14: </Task>
15: - <Task>
16: <s:Id>task_b</s:Id>
17: <s:Version>59589</s:Version>
18: <Subject xsi:type="x:string">Task B</Subject>
19: <StartDate xsi:type="x:dateTime">2008-11-02T12:20:51</StartDate>
20: <DueDate xsi:type="x:dateTime">2008-11-12T12:20:51</DueDate>
21: <Status xsi:type="x:string">In Progress</Status>
22: <Priority xsi:type="x:string">Normal</Priority>
23: <IsComplete xsi:type="x:boolean">false</IsComplete>
24: <PercentComplete xsi:type="x:decimal">0</PercentComplete>
25: </Task>
26: </s:EntitySet>
Запрос можно усложнить. Например, спросив =+DateTime("2008-11-07+00:00:00Z")+select+e'">https://kosinskylabs.data.database.windows.net/v1/tasks?q='from+e+in+entities+where+e["StartDate"]+<=+DateTime"2008-11-07+00:00:00Z")+&&+e["DueDate"]+>=+DateTime("2008-11-07+00:00:00Z")+select+e'. Получим только одну запись.
SOAP интерфейс
Кроме описанного выше REST способа доступа можно воспользоваться SOAP методами, со следующим контрактом:
1: public interface ISitkaSoapService
2: {
3: Scope Create(Scope scope, Entity entity);
4: void Delete(Scope scope);
5: Entity Get(Scope scope);
6: List<Entity> Query(Scope scope, string query1);
7: Scope Update(Scope scope, Entity entity);
8: }
Для Entity используется следующий DataContract:
1: public class Entity : IExtensibleDataObject, INotifyPropertyChanged
2: {
3: public Entity();
4:
5: public ExtensionDataObject ExtensionData { get; set; }
6: [DataMember]
7: public string Id { get; set; }
8: [DataMember]
9: public string Kind { get; set; }
10: [DataMember]
11: public Dictionary<string, object> Properties { get; set; }
12: [DataMember]
13: public long Version { get; set; }
14:
15: public event PropertyChangedEventHandler PropertyChanged;
16:
17: protected void RaisePropertyChanged(string propertyName);
18: }
Как видим у каждой Entity, есть ID, тип и версия, а также набор произвольных свойств. А Authority и Container, есть потомками от него. По этому, в SOAP варианте интерфейса мы имеем всего один метод Create, что создавать определяется текущим контекстом, то есть объектом Scope:
public struct Scope : IExtensibleDataObject, INotifyPropertyChanged
{
[DataMember(IsRequired = true)]
public string AuthorityId { get; set; }
[DataMember]
public string ContainerId { get; set; }
[DataMember]
public string EntityId { get; set; }
public ExtensionDataObject ExtensionData { get; set; }
[DataMember(EmitDefaultValue = false)]
public VersionMatch VersionMatch { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
,есть хотим создать Authority, то не задаем в контекста никаких свойств, если хотим работать с контейнерами, то задаем только AuthorityId и т.д.
В результате код для создания новой задачи выглядит:
1: //Создаем клиент
2: var client = new SitkaSoapServiceClient();
3:
4: var config = SsdsConfig.Create();
5:
6: //И контекст
7: scope = new Scope();
8: scope.AuthorityId = config.Authority;
9: scope.ContainerId = containerId;
10:
11: //Пользователь для подключения
12: client.ClientCredentials.UserName.UserName = config.Username;
13: client.ClientCredentials.UserName.Password = config.Password;
14:
15: //Создаем объект и задаем его основные свойства
16: var entity = new Entity()
17: {
18: Id = task.TaskId,
19: Kind = "Task"
20: };
21:
22: //Все остальные свойства
23: entity.Properties = new Dictionary<string, object>();
24: entity.Properties.Add("Subject", task.Subject);
25: entity.Properties.Add("StartDate", task.StartDate);
26: entity.Properties.Add("DueDate", task.DueDate);
27: entity.Properties.Add("Priority", task.Priority);
28: entity.Properties.Add("Status", task.Status);
29: entity.Properties.Add("PercentComplete", (decimal)task.PercentComplete);
30: entity.Properties.Add("IsComplete", task.IsComplete);
31:
32: //Собственно создаем
33: client.Create(scope, entity);
34:
35: //Закрываем клиента
36: client.Close();
Аналогично при обновлении объекта, вместо строки создания пишем:
1: scope.EntityId = task.TaskId;
2:
3: client.Update(scope, entity);
Запросы
Для получения объектов с сервера мы в метод Query передаем строку в LINQ формате. И конечно при этом мы можем использовать условия для where. Здесь важно помнить про обязательные и не обязательные свойства.
Для обращения к Id или Kind мы используем точку:
from e in entities where e.Kind=="Task" select e
хотя для Kind есть специальная функция OfKind, а для остальных квадратные скобочки
from e in entities where e["Priority"]=="Normal" select e
Также поддерживаются некоторые из функций, например Take:
(from t in entities where t.Kind == "Task" select t).Take(1)
и Join:
from t in entities.OfKind(“Task”)
from u in entities.OfKind(“User”)
where u[“Name”] == t[“AssignedTo”] select t
Все это можно делать без SDK. При наличии SDK и добавлении ссылки на System.Data.Services.Client, можно оперировать в терминах обычных объектов и LINQ запросов. Читать далее...
Читать далее
Категория:
Azure
|