Страница 9 из 15
Аннотированные схемы По своей структуре это обычные XSD-схемы, в которых допускаются специальные аннотации, задающие их привязку к сущностям реляционной структуры: таблицам, полям, первичным и внешним ключам, отношениям между таблицами и т.д., благодаря чему данные, хранящиеся на SQL Server, можно привести к желаемой XSD- структуре и запрашивать затем как XML-документ (XPath, XQuery). В VS .Net входит удобный редактор XSD-схем, позволяющий собирать их, натаскивая drag-n-drop'ом элементы, атрибуты, типы и т.д. из панели инструментов. Редактор имеет две панели: одна показывает традиционный XML-код схемы, а другая - ее реляционный эквивалент в виде таблиц и связей между ними. При переключении происходит автоматическая валидация схемы, доступная также из меню (Schema -> Validate). В нем есть цветовое выделение синтаксических конструкций, intelisense-подсказки и многие другие приятные вещи. Итак, с помощью этого замечательного редактора я создаю вид моего XML-документа, который будет содержать, допустим, информацию по клиентам и сделанных ими заказах. <?xml version="1.0" encoding="utf-8" ?> <xs:schema id="XMLSchema1" xmlns:xs="https://www.w3.org/2001/XMLSchema"> <xs:complexType name="Клиент"> <xs:sequence> <xs:element name="Адрес" type="Адрес" /> <xs:element name="Заказы" type="Заказы" /> </xs:sequence> <xs:attribute name="Имя" type="xs:string" /> <xs:attribute name="Должность" type="xs:string" /> <xs:attribute name="Фирма" type="xs:string" /> </xs:complexType> <xs:complexType name="Адрес"> <xs:sequence> <xs:element name="Страна" type="xs:string" /> <xs:element name="Город" type="xs:string" /> <xs:element name="Улица_дом" type="xs:string" /> <xs:element name="Индекс" type="xs:string" /> </xs:sequence> </xs:complexType> <xs:complexType name="Заказы"> <xs:sequence> <xs:element name="Заказ" type="Заказ" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> <xs:complexType name="Заказ"> <xs:sequence> <xs:element name="Дата" type="xs:date" /> <xs:element name="Стоимость" type="xs:float" /> </xs:sequence> </xs:complexType> </xs:schema> Рис.3 Теперь, чтобы по этой схеме представить данные из SQL Server, сопоставим их элементам и атрибутам при помощи аннотаций - см. рис.4 - для поддержки которых в схеме делается ссылка на соответствующее пространство имен (xmlns:ms="urn:schemas-microsoft-com:mapping-schema"). <?xml version="1.0" encoding="utf-8" ?> <xs:schema id="SQLSchema1" xmlns:xs="https://www.w3.org/2001/XMLSchema" xmlns:ms="urn:schemas-microsoft-com:mapping-schema"> <xs:annotation> <xs:appinfo> <ms:relationship name="CustOrds" parent="Customers" parent-key="CustomerID" child="Orders" child-key="CustomerID" /> </xs:appinfo> </xs:annotation> <xs:complexType name="Клиент"> <xs:sequence> <xs:element name="Адрес" type="Адрес" ms:is-constant="1" /> <xs:element name="Заказы" type="Заказы" ms:is-constant="1" /> </xs:sequence> <xs:attribute name="Имя" type="xs:string" ms:field="ContactName" /> <xs:attribute name="Должность" type="xs:string" ms:field="ContactTitle" /> <xs:attribute name="Фирма" type="xs:string" ms:field="CompanyName" /> </xs:complexType> <xs:complexType name="Адрес"> <xs:sequence> <xs:element name="Страна" type="xs:string" ms:field="Country" /> <xs:element name="Город" type="xs:string" ms:field="City" /> <xs:element name="Улица_дом" type="xs:string" ms:field="Address" /> <xs:element name="Индекс" type="xs:string" ms:mapped="false" /> </xs:sequence> </xs:complexType> <xs:complexType name="Заказы"> <xs:sequence> <xs:element name="Заказ" type="Заказ" minOccurs="0" maxOccurs="unbounded" ms:relation="Orders" ms:relationship="CustOrds" /> </xs:sequence> </xs:complexType> <xs:complexType name="Заказ"> <xs:sequence> <xs:element name="Дата" type="xs:date" ms:field="OrderDate" /> <xs:element name="Стоимость" type="xs:float" ms:field="Freight" /> </xs:sequence> <xs:attribute name="Номер" type="xs:string" ms:field="OrderID" /> </xs:complexType> <xs:element name="Клиент" type="Клиент" ms:relation="Customers" /> </xs:schema> Рис.4 Аннотация sql:relation используется для отображения узла на таблицу. Она не поддерживается в тэгах определения типа, т.е. только в xs:element и xs:attribute, поэтому нам пришлось ввести в схему элемент составного типа "Клиент": <xs:element name="Клиент" type="Клиент" ms:relation="Customers" />. Вложенные элементы соответствуют записям дочерней таблицы, поэтому для них требуется еще задать ms:relationship. Отношения между таблицами в терминах родительской и дочерней таблиц (parent / child) и полей, по которым устанавливается связь (parent-key / child-key), определяются как атрибуты элемента <ms:relationship> в разделе определения аннотаций <xs:annotation>, <xs:appinfo>. Затем это отношение можно использовать, чтобы вложить дочерние записи внутрь родительского элемента <xs:element name="Заказ" type="Заказ"... ms:relation="Orders" ms:relationship="CustOrds"/>. Если вложенный элемент не соответствует никакой дочерней таблице, а несет чисто контейнерную функцию (как, например, Адрес), то он помечается атрибутом ms:is-constant="1": <xs:element name="Адрес" type="Адрес" ms:is-constant="1"/>. Аннотация ms:field привязывает XML-узел к полю таблицы. Она не требуется, когда название атрибута совпадает с названием поля. Непривязанные атрибуты также не допускаются. Если мы не планируем брать значение узла из БД, но в силу каких-либо причин не можем исключить его из схемы, его нужно пометить аннотацией ms:mapped="false": <xs:element name="Индекс" type="xs:string" ms:mapped="false" />. От is-constant она отличается тем, что узел вообще исключается из результирующего XML-документа. Разберем еще несколько аннотаций на примере схемы, которая воссоздает по таблице parent-child дерево иерархии в виде XML: <?xml version="1.0" encoding="utf-8" ?> <xs:schema xmlns:xs="https://www.w3.org/2001/XMLSchema" xmlns:ms="urn:schemas-microsoft-com:mapping-schema" id="SQLSchema2"> <xs:annotation> <xs:appinfo> <ms:relationship name="Начальник-Подчиненный" parent="Employees" parent-key="EmployeeID" child="Employees" child-key="ReportsTo" /> </xs:appinfo> </xs:annotation> <xs:element name="Сотрудник" type="Тип_Сотрудник" ms:relation="Employees" ms:key-fields="EmployeeID" ms:limit-field="ReportsTo" /> <xs:complexType name="Тип_Сотрудник"> <xs:sequence> <xs:element name="Сотрудник" type="Тип_Сотрудник" ms:relation="Employees" ms:key-fields="EmployeeID" ms:relationship="Начальник-Подчиненный" ms:max-depth="3" /> </xs:sequence> <xs:attribute name="ID_сотрудника" type="xs:ID" ms:field="EmployeeID" ms:hide="true" /> <xs:attribute name="Имя" type="xs:string" ms:field="FirstName" /> <xs:attribute name="Фамилия" type="xs:string" ms:field="LastName" /> </xs:complexType> </xs:schema> Рис.5 Он взят из документации к SQLXML 3.0. Таблица Employees содержит записи по сотрудникам и связана сама с собой, т.е. в поле ReportsTo для каждого сотрудника указывается EmployeeID его начальника. ms:max-depth задает максимальную глубину вложенности рекурсии при раскрытии отношения "родитель-потомок". В отличие от предыдущей ситуации, где количество уровней в иерархии определялось длиной максимальной ветки связанных таблиц, глубина дерева в случае parent-child таблицы зависит от ветки, по которой мы идем от корня, и априори неочевидна. Не обладая в текущей версии специальным оператором построения иерархии по такому типу связи, SQL Server разрешает ее в последовательность соединенных UNION'ами SELECT'ов, каждый из которых соответствует уровню иерархии. Поэтому их число (ms:max-depth) SQL Server должен знать заранее. Максимальное значение для него волевым образом установлено в 50. Другая аннотация - ms:limit-field - позволяет провести ограничение (WHERE) по какому-либо полю еще на уровне схемы, т.е. до того, как дело дойдет до XPath. Обычно она употребляется в паре с ms:limit-value, которая задает значение критерия. В данном случае эта аннотация опущена, что означает, что по умолчанию берется значение NULL. Таким образом, верхним уровнем в данной иерархии будут самые-самые начальники (у которых начальников нет: ReportsTo = Null). Почему атрибут ID_сотрудника аннотирован как ms:hide="true"? Он несет чисто служебную информацию и вряд ли понадобится в XML-результате. Но его не хочется выключать из схемы при помощи ms:mapped="false", потому что он действительно привязан к информации в БД, которая понадобится в дальнейшем. Например, он может фигурировать в критерии XPath-запроса: cmd.CommandText = "Сотрудник[@ID_сотрудника='4']" (Чтобы этот запрос возвратил сотрудника с EmployeeID = 4, нужно убрать фильтрацию в схеме - ms:limit-field). Наконец, еще одна аннотация, которая сейчас необязательна, но встретится нам через параграф - это ms:key-fields. Она задает значения полей, составляющих первичный ключ таблицы. Полный список аннотаций, естественно, не ограничивается теми, которые встретились в этих двух простых примерах схем. Он достаточно обширен и позволяет строить довольно нетривиальные соответствия между XML-схемой и реляционным содержанием. Его можно найти в документации на SQLXML 3.0. static void Annotated_XPathQuery_SQLXML() { ... cmd.CommandText = "Клиент[Адрес/Страна='Spain' or Адрес/Страна='France']"; cmd.SchemaPath = "..\\Schemas\\SQLSchema1.xsd"; cmd.CommandType = SqlXmlCommandType.XPath; cmd.RootTag = "Клиенты"; XmlDocument xml = new XmlDocument(); xml.Load(cmd.ExecuteStream()); ... } Скрипт 10 В Скрипте 10, как и в предыдущем примере (Скрипт 9), на SQL Server посылается XPath-запрос, однако теперь данные рассматриваются через призму выбранной аннотированной схемы (указывается в свойстве SqlXmlCommand.SchemaPath) и трактуются в соответствии с задаваемой ею структурой. В данном случае запрос выбирает всех клиентов из Испании и Франции и сделанные ими заказы. Встроенной поддержкой XPath (а также XQuery) SQL Server в настоящее время не располагает, поэтому XPath по дороге превращается в то, что ему более понятно, а именно - в SQL-запрос. Если быть совсем точным, то в запрос типа FOR XML EXPLICIT. Ради любопытства можете открыть Profiler и посмотреть его для Скрипта 10 (здесь я его приводить не буду, потому что он занял бы еще как минимум страницу). Поддерживается подмножество стандартного синтаксиса XPath в части осей, функций и операторов. Отрадно, что каждым SQLXML веб-релизом это подмножество расширяется. |