Visual Basic, .NET, ASP, VBScript
 

   
 
Описание для автора не найдено
 
     
   
 
7.2 XML Query архитектура
Автор: Захаров Андрей (zamer@lavitese.net), Ukraine .Net Alliance (http://www.uneta.org/)
Апрель 2004
Применяется к:
  • Microsoft .Net Framework 1.2
  • XQuery
  • XML
  • Обзор:
    XQuery – это мощный и удобный язык, предназначенный для обработки XML. Под XML понимаются не только файлы в XML-формате, но и другие данные, похожие на XML, включая базы данных, структура которых представляет собой вложенные, поименованные деревья с атрибутами.
    Содерджание:
  • Введение
  • Общая архитектура XML запросов
  • 7.2.1 Работа с низкоуровневым API
  • 7.2.2 Работа с высокоуровневым API
  • Класс XmlCommand
  • Приложение
  • Введение

    XQuery – это мощный и удобный язык, предназначенный для обработки XML. Под XML понимаются не только файлы в XML-формате, но и другие данные, похожие на XML, включая базы данных, структура которых представляет собой вложенные, поименованные деревья с атрибутами. Рабочая группа, образованная консорциумом W3C продолжает разработку первой официальной версии этого языка XQuery 1.0, представляя промежуточные результаты в серии предварительных документов с которыми можно познакомиться по адресу: http://www.w3.org/TR/xquery/. (http://www.w3.org/TR/xquery/)

    Новая версия Microsoft .Net Framework 1.2 опирается на рабочий проект языка XQuery описанный в документе http://www.w3.org/TR/2002/WD-xquery-20021115/. (http://www.w3.org/TR/2002/WD-xquery-20021115/)

    Архив примеров (http://www.uneta.org/Other/XMLQueryArchitect.zip)

    Общая архитектура XML запросов

    Диаграмма на Рисунке 1 показывает Общую Архитектуру запроса к XML. Для каждого языка XML-запросов, есть компилятор, который генерирует код на промежуточном языке для запроса представленного классом XmlExpression.

    Рисунок 1. Общая Архитектура XML запроса для множества языков XML-запросов

    Несколько объектов класса XmlExpression могут быть объединены вместе в одном запросе и однажды откомпилированы, а затем использоваться в других запросах. Это возможно благодаря классу XmlViewSchema содержащему представление XML. В действительности представление XML компилируется точно так же как и инструкции языка XML-запросов и добавляется в XQuery-запрос всякий раз, когда используется функция map:view(). Такой промежуточный формат обеспечивает уровень абстракции для любого языка XML- запросов.

    После компиляции и объединения управление передается исполнителю запроса. На этом этапе происходит оптимизация сгенерированного запроса идентично обработке запроса SQL для перезаписей в реляционной базе данных, чтобы получить лучшее выполнение запроса.

    Исполнитель запроса генерирует исполнимый код на языке MSIL и инструкции SQL, если источники данных расположены на SQL Сервере, используя класс XmlCommand. Класс XmlCommand– это план запроса, который может быть кэширован и выполнен многократно для указанных источников данных, вызывая метод Execute(). Вывод исполненных запросов происходит в поток XML (или в файлы другого формата) через класс TextWriter.

    Наличие архитектуры запроса, которая является общей для всех языков XML-запросов, дает возможность воспользоваться преимуществом независимой от языка оптимизации, и, следовательно, обеспечивает дальнейшее усовершенствование технологии.

    7.2.1 Работа с низкоуровневым API

    Для разработчиков технология XQuery предусматривает высокоуровневую и низкоуровневую архитектуру XQuery API. Низкоуровневая архитектура XQuery API не реализована в .NET Framework 1.2, поэтому дальнейшее изложение будет касаться только высокоуровневой XQuery API архитектуры.

    7.2.2 Работа с высокоуровневым API

    Для написания программ с использованием высокоуровневой архитектуры XQuery API, согласно документации .NET Framework SDK 1.2, разработчику необходим только класс XQueryProcessor, который обеспечивает компиляцию и исполнение XML-запросов. Прежде, чем показать, как мы можем использовать класс XQueryProcessor рассмотрим его свойства и методы. Как видно из Таблицы 1 у класса есть единственный конструктор, три свойства, и четыре метода.

    Конструктор Описание
    XQueryProcessor() Создает новый экземпляр класса XQueryProcessor
    Свойства Описание
    XmlCommand Ссылка на экземпляр класса XmlCommand, представляющий собой откомпилированный запрос, который может быть многократно выполнен.
    XmlViewSchemaDictionary Ссылка на экземпляр класса XmlViewSchemaDictionary который используется, когда представление XML необходимо в течение компиляции запроса с использованием функции map:view().
    SqlQueryOptions Ссылка на экземляр класса SqlQueryOptions который принимает одно из значений перечисления SqlQueryPlan - Mars, Serialized, and UnionAll. По умолчанию, XQueryProcessor использует значение Serialized.
    Методы Описание
    Compile(query) Компилирует запрос(параметр query) представляющий собой экземпляр класса XmlCommand.
    CompileView(query, mapping-location) Компилирует указанный запрос(параметр query) и представление XML, указанное параметром mapping-location. Этот метод используется для компиляции запросов к отдельной базе данных с единственным XML представлением и используется вместе с методом ExecuteView().
    Execute(datasources, results) Исполняет откомпилированный XQuery-запрос к одному или нескольким источникам данных, представленных экземпляром класса XmlResolver (параметр datasources) и выводит результат в экземпляре класса TextWriter, (параметр results). Метод вызывается после того, как метод Compile() завершил свою работу.
    Execute (context-document-uri, datasources, results) Как и предыдущий метод, но если в запросе используется контекст документа (т.е., “/” или функция input() языка XQuery). Контекст документа указывается в параметре context-document-uri, с использованием перегрузки метода.
    ExecuteView (connection,results) Метод вызывается после того, как выполнился метод CompileView(). Через параметр connection осуществляется соединение с источником данных, а результаты возвращаются в параметре results (экземпляр класса TextWriter).

    Таблица 1. Описание членов класса XQueryProcessor

    Класс XmlCommand

    Класс XmlCommand выполняет XML-запрос и используется как свойство классов XsltProcessor и XQueryProcessor. Вы обеспечиваете класс XmlCommand источниками данных и параметрами, необходимыми для выполнения запроса. Класс предоставляет единственный метод Execute(), описанный далее в Таблице 2, который может использоваться для выполнения множественных запросов к источнику данных любого типа.

    Метод Описание
    Execute(context-document-uri, datasources, results) Исполняет откомпилированный XQuery-запрос к одному или более источникам данных (параметр datasources), которые представлены экземпляром класса XmlResolver, и выводит результаты в экземпляр класса TextWriter (параметр results). Метод вызывается после того, как метод Compile() завершил свою работу. Если запрос использует контекст документа (т.е., “/” или функцию input()языка XQuery), необходимо передавать контекст документа через параметр context-document-uri.

    Таблица 2. Описание метода Execute() класса XmlCommand

    Прежде чем приступить к рассмотрению примеров, иллюстрирующих применение класса XqueryProcessor обратимся к краткой характеристике функций и операторов языка XQuery поддерживаемых .NET Framework 1.2.

    Как следует из документации к NET Framework SDK 1.2 простой XQuery-запрос обычно состоит из ссылки на документ (чаще всего представляющую собой путь к документу). Спецификация XQuery предлагает несколько способов ввода данных XML с использованием следующих функций: document(), input(), collection(), а также дополнительной функции map:view(), которая используется при обращении к XML-представлению реляционных баз данных SQL Сервера.

    Функция document() возвращает документ(ы), к которому производится запрос и связана с объектом XmlUrlResolver который определяет название документа вместе с путем к файлу или путем URL.

    declare namespace PD='http://www.adventure-works.com/schemas/products/description'
    <Root> 
    {document('http://server/.../XMLDoc.xml')/PD:ProductDescription/
    PD:Summary }
    </Root>
    

    или

    <Root> 
    {document(c:\MyFolder\...\XMLDoc.xml')/PD:ProductDescription/
    PD:Summary}
    </Root> 
    

    При использовании класса XmlDataSourceResolver можно определить символический псевдоним документа как показано ниже:

    declare namespace PD='http://www.adventure-works.com/schemas/products/description'
    <Root>
    {document('XMLDoc')/PD:ProductDescription/PD:Summary}
    </Root>
    

    Функция input() возвращает входную последовательность. Например:

    declare namespace PD='http://www.adventure-works.com/schemas/products/description'
    <Root>
    {input()/PD:ProductDescription/PD:Summary }
    </Root>
    

    Обратите внимание, что функция input() в запросе не идентифицирует документ к которому производится запрос, вместо этого в запросе использован контекст документа. Контекст документа должен быть определен в Execute() функции.

    Можно также создавать многократные запросы к документам как показано ниже:

    declare namespace PD='http://www.adventure-works.com/schemas/products/description'
    <Root>
     <Query1>
      { document('XmlDoc')/PD:ProductDescription/PD:Features }
     </Query1>
     <Query2>
      {input()/PD:ProductDescription/PD:Summary }
     </Query2>
    </Root>
    

    В архитектуре XML-запроса документ, к которому производится запрос, может представлять собой XML-документ или XML-представление реляционных данных SQL Сервера. При использовании XML-представления необходимо создать объект SqlConnection и определить строку подключения к источнику данных. После этого добавить это подключение к объекту XmlDataSourceResolver перед выполнением запроса.

    XQuery функции и операторы для Whidbey “Alpha” определены в рабочем проекте по адресу: http://www.w3.org/TR/2002/WD-XQuery-operators-20021115/ (http://www.w3.org/TR/2002/WD-XQuery-operators-20021115/ ) .

    XQuery функции содержат “fn:” префикс, который указывает на пространство имен ‘http://www.w3.org/TR/2002/WD-XQuery-operators-20021115/’ и автоматически связывает их с XQuery процессором. Это позволяет использовать любые функции XQuery непосредственно в XQuery выражениях без предварительного объявления их в XQuery-прологе как показано ниже:

    declare namespace PD='http://www.adventure-works.com/schemas/products/description' 
    <Root>
    {fn:upper-case(document('XMLDoc')/PD:ProductDescription
    /PD:Summary)}
    </Root>
    

    Операторы XQuery используемые в XQuery-выражениях подразделяются в зависимости от типа операндов на:

    • Операторы простой арифметики над числовыми данными;
    • Логические операторы;
    • Операторы для выполнения операций над последовательностями.

    Для операторов XQuery также оперделен префикс “op:”, но на практике проще использовать неявный синтаксис операторов. По этой причине, поддержка для явного синтаксиса (op:func()) не была реализована в Whidbey “Alpha”.

    Следующий пример иллюстрирует использование оператора XQuery “+”, чтобы увеличить значение элемента на единицу.

    declare namespace PD='http://www.adventure-works.com/schemas/products/description' 
    <Root>
    { let $ProdID := fn:data(document('XMLDoc')/PD:ProductDescription/
    @ProductModelID)
    return cast as xs:integer($ProdID) + 1
    }
    </Root>
    

    Рассмотрим на примерах использование класса XQueryProcessor.

    Пример представленный ниже использует Xml-документ, находящийся в файле books.xml (представленный в Приложении к статье) для запроса книг по критерию используя XQueryProcessor класс.

    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.Query;
    using System.Data.SqlXml;
    
    namespace SamplesXQuery
    {
      class XQueryExample1
       {
          [STAThread]
          static void Main (string[] args)
          {
             XQueryXMLDocument();
          }
          
          public static void XQueryXMLDocument()
          {
    //Файл с результатами
    StreamWriter writer = new StreamWriter("output.xml");
    
    	   try
            {	
             //Инициализация XmlDataSourceResolver
             XmlDataSourceResolver datasource=new XmlDataSourceResolver();  
    	    datasource.Add("bookstore", "books.xml");
    
    		
    		//Запрос
    string query  = "<bookstore> { for $b in document('bookstore')/bookstore/book where $b/@genre='philosophy' and $b/@publicationdate='1991'
    return $b/title } </bookstore>";
    		
    // Инициализация XQueryProcessor
    		XQueryProcessor xq = new XQueryProcessor();  
    		//Компиляция и выполнение запроса
    		xq.Compile(query);
    		xq.Execute(datasource, writer);
    	    }
             catch (Exception e)
             {
                Console.WriteLine ("Exception: " + e.ToString ());
             }
             finally
             {
                writer.Close();
             }
    	       
          }
        
       }
    }
    

    Сначала создается объект XmlDataSourceResolver для связи с источником данных (файл books.xml). Это позволяет в функции document(), являющейся частью XQuery-запроса ссылаться на этот источник через псевдоним bookstore. Далее формируется запрос, представленный значением строковой переменной, который выбирает записи, у которых значения атрибутов genre и publicationdate равны соответственно philosophy и 1991. После этого запрос компилируется и выполняется с использованием методов Compile() и Execute() класса XQueryProcessor. Результат выводится в файл output.xml.

    Класс XPathDocument2 предназначен для эффективной работы с XQuery. В следующем примере создается запрос к двум Xml-документам. Этот пример идентичен предыдущему, за исключением того, что документ books2.xml загружен через XPathDocument2 и затем добавлен к XmlDataSourceResolver. Такой способ может быть полезен, когда вы хотите редактировать документ с использованием XPathEditor перед выполнением XQuery-запроса.

    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.Query;
    using System.Data.SqlXml;
    
    namespace SamplesXQuery
    {
      class XQueryExample2
       {
          [STAThread]
          static void Main (string[] args)
          {
             XQueryXMLDocument();
          }
          public static void XQueryXMLDocument()
          {
           StreamWriter writer =new StreamWriter("output.xml");
    	  try
           {
    	   // Инициализация XmlDataSourceResolver
    	   XmlDataSourceResolver datasource =new XmlDataSourceResolver();
    	   datasource.Add("bookstore", "books.xml");
            XPathDocument2 doc =new XPathDocument2(datasource.NameTable);
    	   doc.Load("books2.xml");
    	   datasource.Add("bookstore2", doc.CreateXPathNavigator2());
            // Запрос
            string query = "<bookstore> { for $b in document('bookstore')
            /bookstore/book where $b/@genre='philosophy' 
            and $b/@publicationdate='1991' return $b/title }
            { for $b2 in document('bookstore2')/bookstore/book where               
            $b2/@genre='autobiography' and $b2/@publicationdate='1981'
            return $b2/title } </bookstore>";
    	   // Инициализация XQueryProcessor
    	   XQueryProcessor xq =new XQueryProcessor();
            // Компилировать и выполнить запрос
            xq.Compile(query);
    	   xq.Execute(datasource, writer);
    	}
             catch (Exception e)
             {
                Console.WriteLine ("Exception: " + e.ToString ());
             }
             finally
             {
               writer.Close();
             }
          }
       }
    }
    

    XQuery-запрос, используемый в этом примере, находит все книги со значением атрибута genre равным philosophy и значением атрибута publicationdate равным 1991 из файла bookstore.xml и все книги со значением атрибута genre равным autobiography и значением атрибута publicationdate равным 1981 из файла bookstore2.xml.

    Следующий пример использует функцию map:view(), чтобы сделать запрос к данным SQL Сервера через XML-представление. Источник данных добавляется к XmlDataSourceResolver через класс SqlConnection. XML-представление файла данных через псевдоним nwind добавляется к XmlViewSchemaDictionary и затем используется в функции map:view() чтобы выполнить XQuery-запрос.

    XmlViewSchemaDictionary представляет собой набор объектов XmlViewSchema с помощью которых метод Add() добавляет XML-представления используемые для запросов и модификации данных SQL Сервера. Вы можете передавать ссылки на файл MSD (.msd) методу Add(), который автоматически создает новый объект XmlViewSchema, компилирует XML-представление и добавляет результат к XmlViewSchemaDictionary. Таким образом, класс XmlViewSchema представляет собой откомпилированное XML-представление.

    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.Query;
    using System.Data.SqlXml;
    using System.Data.SqlClient;
    
    namespace SamplesXQuery
    {
      class XQueryExample3
       {
          
         [STAThread]
         static void Main (string[] args)
         {
            XQuerySqlData();
         }
          
         public static void XQuerySqlData()
         {
           //Создать соединение с данными SQL сервера
           SqlConnection myConn =new SqlConnection("server="My Server";
           database=northwind;Integrated Security=SSPI");
           StreamWriter writer =new StreamWriter("output.xml");
    	  try
           {
    	   myConn.Open();
    	   //Инициализация XQueryProcessor
    	   XQueryProcessor xq =new XQueryProcessor();
            //Добавление XML-представления к XmlViewSchemaDictionary 
    	   xq.XmlViewSchemaDictionary.Add("nwind", new      
            XmlTextReader("northwind.msd"));
    
    	   //Добавление источника данных к XmlDataSourceResolver
    	   XmlDataSourceResolver datasource =new XmlDataSourceResolver();
    	   datasource.Add("Northwind", myConn);
            //Запрос
            string query = "declare namespace        
            map='http://schemas.microsoft.com/xml/2002/09/28/
            mappingfunctions'<Root>{map:view('nwind')/Customers}</Root>";
      
    	   xq.Compile(query);
    	   xq.Execute(datasource, writer);
            
    	  }
           catch (Exception e)
           {
             Console.WriteLine ("Exception: " + e.ToString ());
           }
           finally
           {
             writer.Close();
    	    myConn.Close();
           }
    
          }
       }
    }
    

    XQuery-запрос представленный в примере возвращает всех заказчиков (Customers) из базы данных Northwind используя ее Xml-представление.

    Xml-схема для Xml-представления Northwind:

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <xsd:element name="Customers">
     <xsd:complexType>
     <xsd:sequence>
     <xsd:element name="Customer">
      <xsd:complexType>
       <xsd:sequence>
        <xsd:element name="Order">
         <xsd:complexType>
          <xsd:sequence>
           <xsd:element name="Product">
            <xsd:complexType>
             <xsd:attribute name="UnitQuantity" type="xsd:integer"/>
             <xsd:attribute name="ProductName" type="xsd:string"/>
             <xsd:attribute name="UnitPrice"  type="xsd:string" />
             <xsd:attribute name="QuantityPerUnit" type="xsd:string"/>
            </xsd:complexType>
           </xsd:element>
          </xsd:sequence>
          <xsd:attribute name="CustomerID" type="xsd:string" />
          <xsd:attribute name="orderid" type="xsd:string"/>
          <xsd:attribute name="OrderDate" type="xsd:string"/>
          <xsd:attribute name="ShipCity" type="xsd:string"/>
         </xsd:complexType>
        </xsd:element>
       </xsd:sequence>
       <xsd:attribute name="name" type="xsd:string" />
       <xsd:attribute name="CustomerID" type="xsd:string" />
       <xsd:attribute name="Country" type="xsd:string" />
       <xsd:attribute name="City" type="xsd:string" />
      </xsd:complexType>
     </xsd:element>
     </xsd:sequence>
     </xsd:complexType>
     </xsd:element>
    </xsd:schema>
    

    Файл msd-схемы для Xml-представления Northwind:

    <m:MappingSchema
      xmlns:m="http://schemas.microsoft.com/data/2002/09/28/mapping">
     <m:DataSources>
      <m:DataSource Name="XML" Direction="Target" Type="Xml">
       <m:Schema Location="northwind.xsd" />
      </m:DataSource>
      <m:DataSource Name="Northwind" Direction="Source" Type="SQL Server">
       <m:Schema Location="northwind.rsd" />
       <m:Variable Name="Orders" Select="Orders" />
       <m:Variable Name="Customers" Select="Customers"/>
       <m:Variable Name="Products" Select="Products"/>
       <m:Variable Name="OrderDetails" Select="Order Details"/>
       <m:Relationship Name="rel1" FromVariable="Customers"
                       ToVariable="Orders">
        <m:FieldJoin From="CustomerID" To="CustomerID" />
       </m:Relationship>
       <m:Relationship Name="rel2" FromVariable="Orders"
                       ToVariable="OrderDetails">
        <m:FieldJoin From="OrderID" To="OrderID" />
       </m:Relationship>
       <m:Relationship Name="rel3" FromVariable="OrderDetails"
                       ToVariable="Products">
        <m:FieldJoin From="ProductID" To="ProductID" />
       </m:Relationship>
      </m:DataSource>
     </m:DataSources>
     
     <m:Mappings>
      <m:Map SourceVariable="Customers"
             TargetSelect="/Customers/Customer">
       <m:FieldMap SourceField="CompanyName" TargetField="@name" />
       <m:FieldMap SourceField="CustomerID" TargetField="@CustomerID" />
       <m:FieldMap SourceField="Country" TargetField="@Country" />
       <m:FieldMap SourceField="City" TargetField="@City" />
      </m:Map>
      <m:Map SourceVariable="Orders"
             TargetSelect="/Customers/Customer/Order">
       <m:FieldMap SourceField="orderid" TargetField="@orderid" />
       <m:FieldMap SourceField="CustomerID" TargetField="@CustomerID" />
       <m:FieldMap SourceField="OrderDate" TargetField="@OrderDate" />
       <m:FieldMap SourceField="ShipCity" TargetField="@ShipCity" />
      </m:Map>
     </m:Mappings>
    </m:MappingSchema>
    

    Файл rsd-схемы для Xml-представления Northwind:

    <rsd:Database Owner="sa" Name="Northwind"
         xmlns:rsd="http://schemas.microsoft.com/data/2002/09/28/rsd">
     <rsd:Schema Name="dbo">
     <rsd:Tables>
      <rsd:Table Name="Orders">
       <rsd:Columns>
        <rsd:Column Name="OrderID" AutoIncrement="1" IncrementStep="1"
                    SqlType="int" IncrementSeed="1" />
        <rsd:Column Name="CustomerID" SqlType="nchar" AllowDbNull="1"
                    Length="5" />
        <rsd:Column Name="EmployeeID" SqlType="int" AllowDbNull="1" />
        <rsd:Column Name="OrderDate" SqlType="datetime" AllowDbNull="1" />
        <rsd:Column Name="RequiredDate" SqlType="datetime"
                    AllowDbNull="1"/>
        <rsd:Column Name="ShippedDate" SqlType="datetime"
                    AllowDbNull="1"/>
        <rsd:Column Name="ShipVia" SqlType="int" AllowDbNull="1" />
        <rsd:Column Name="Freight" SqlType="money" AllowDbNull="1" />
        <rsd:Column Name="ShipName" SqlType="nvarchar" AllowDbNull="1"
                    Length="40" />
        <rsd:Column Name="ShipAddress" SqlType="nvarchar" AllowDbNull="1"
                    Length="60" />
        <rsd:Column Name="ShipCity" SqlType="nvarchar" AllowDbNull="1"
                    Length="15" />
        <rsd:Column Name="ShipRegion" SqlType="nvarchar" AllowDbNull="1"
                    Length="15" />
        <rsd:Column Name="ShipPostalCode" SqlType="nvarchar"
                    AllowDbNull="1" Length="10" />
        <rsd:Column Name="ShipCountry" SqlType="nvarchar" AllowDbNull="1"
                    Length="15" />
       </rsd:Columns>
        <rsd:Constraints>
         <rsd:PrimaryKey Name="PK_Orders">
          <rsd:ColumnRef Name="OrderID" />
         </rsd:PrimaryKey>
         <rsd:ForeignKey Name="FK_Orders_Customers"
                         ForeignTable="Customers"
                         UpdateRule="None" DeleteRule="None">
          <rsd:ColumnMatch Name="CustomerID" ForeignName="CustomerID" />
         </rsd:ForeignKey>
        </rsd:Constraints>
       </rsd:Table>
       <rsd:Table Name="Products">
        <rsd:Columns>
         <rsd:Column Name="ProductID" AutoIncrement="1" IncrementStep="1"
                     SqlType="int" IncrementSeed="1" />
         <rsd:Column Name="ProductName" SqlType="nvarchar" Length="40" />
         <rsd:Column Name="SupplierID" SqlType="int" AllowDbNull="1" />
         <rsd:Column Name="CategoryID" SqlType="int" AllowDbNull="1" />
         <rsd:Column Name="QuantityPerUnit" SqlType="nvarchar"
                     AllowDbNull="1" Length="20" />
         <rsd:Column Name="UnitPrice" SqlType="money" AllowDbNull="1" />
         <rsd:Column Name="UnitsInStock" SqlType="smallint"
                     AllowDbNull="1"/>
         <rsd:Column Name="UnitsOnOrder" SqlType="smallint"
                     AllowDbNull="1"/>
         <rsd:Column Name="ReorderLevel" SqlType="smallint"
                     AllowDbNull="1"/>
         <rsd:Column Name="Discontinued" SqlType="bit" />
        </rsd:Columns>
        <rsd:Constraints>
         <rsd:PrimaryKey Name="PK_Products">
          <rsd:ColumnRef Name="ProductID" />
         </rsd:PrimaryKey>
        </rsd:Constraints>
       </rsd:Table>
       <rsd:Table Name="Order Details">
        <rsd:Columns>
         <rsd:Column Name="OrderID" SqlType="int" />
         <rsd:Column Name="ProductID" SqlType="int" />
         <rsd:Column Name="UnitPrice" SqlType="money" />
         <rsd:Column Name="Quantity" SqlType="smallint" />
         <rsd:Column Name="Discount" SqlType="real" />
        </rsd:Columns>
        <rsd:Constraints>
         <rsd:PrimaryKey Name="PK_Order_Details">
          <rsd:ColumnRef Name="OrderID" />
          <rsd:ColumnRef Name="ProductID" />
         </rsd:PrimaryKey>
         <rsd:ForeignKey Name="FK_Order_Details_Orders"
              ForeignTable="Orders" UpdateRule="None" DeleteRule="None">
          <rsd:ColumnMatch Name="OrderID" ForeignName="OrderID" />
         </rsd:ForeignKey>
         <rsd:ForeignKey Name="FK_Order_Details_Products"
              ForeignTable="Products" UpdateRule="None" DeleteRule="None">
          <rsd:ColumnMatch Name="ProductID" ForeignName="ProductID" />
         </rsd:ForeignKey>
        </rsd:Constraints>
       </rsd:Table>
       <rsd:Table Name="Customers">
        <rsd:Columns>
         <rsd:Column Name="CustomerID" SqlType="nchar" Length="5" />
         <rsd:Column Name="CompanyName" SqlType="nvarchar" Length="40" />
         <rsd:Column Name="ContactName" SqlType="nvarchar" AllowDbNull="1"
                     Length="30" />
         <rsd:Column Name="ContactTitle" SqlType="nvarchar"
                     AllowDbNull="1"
                     Length="30" />
         <rsd:Column Name="Address" SqlType="nvarchar" AllowDbNull="1"
                     Length="60" />
         <rsd:Column Name="City" SqlType="nvarchar" AllowDbNull="1"
                     Length="15" />
         <rsd:Column Name="Region" SqlType="nvarchar" AllowDbNull="1"
                     Length="15" />
         <rsd:Column Name="PostalCode" SqlType="nvarchar" AllowDbNull="1"
                     Length="10" />
         <rsd:Column Name="Country" SqlType="nvarchar" AllowDbNull="1"
                     Length="15" />
         <rsd:Column Name="Phone" SqlType="nvarchar" AllowDbNull="1"
                     Length="24" />
         <rsd:Column Name="Fax" SqlType="nvarchar" AllowDbNull="1"
                     Length="24" />
        </rsd:Columns>
        <rsd:Constraints>
         <rsd:PrimaryKey Name="PK_Customers">
          <rsd:ColumnRef Name="CustomerID" />
         </rsd:PrimaryKey>
        </rsd:Constraints>
       </rsd:Table>
      </rsd:Tables>
     </rsd:Schema>
    </rsd:Database>
    

    Следующий пример использует множественные Xml-представления используя функцию map:view() для запроса данных SQL Сервера. Для каждого XML- представления файл схемы (.msd) добавлен к XmlViewSchemaDictionary и упомянут в функции map:view() при выполнении XQuery-запроса. В этом примере Xml-представление базы Northwind разбито на два отдельных Xml-представления: одно для заказчиков (Customers) с именем customersView и одно для заказов (Orders) с именем ordersView. Далее приведены Xml-схемы и msd-схемы этих Xml-представлений. RSD файл базы Northwind остается неизменным.

    Xml-схема для Xml-представления customersView:

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <xsd:element name="Customer">
        <xsd:complexType>
          <xsd:attribute name="name" type="xsd:string"/>
          <xsd:attribute name="CustomerID" type="xsd:string"/>
          <xsd:attribute name="Country" type="xsd:string"/>
          <xsd:attribute name="City" type="xsd:string"/>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
    

    Файл msd-схемы для Xml-представления customersView:

    <m:MappingSchema
        xmlns:m="http://schemas.microsoft.com/data/2002/09/28/mapping">
      <m:DataSources>
        <m:DataSource Name="XML" Direction="Target" Type="Xml">
          <m:Schema Location="customers.xsd"/>
        </m:DataSource>
        <m:DataSource Name="Northwind" Direction="Source"
                      Type="SQL Server">
          <m:Schema Location="northwind.rsd"/>
          <m:Variable Name="Customers" Select="Customers"/>
        </m:DataSource>
      </m:DataSources>
      <m:Mappings>
        <m:Map SourceVariable="Customers" TargetSelect="/Customer">
          <m:FieldMap SourceField="CompanyName" TargetField="@name"/>
          <m:FieldMap SourceField="CustomerID"
                      TargetField="@CustomerID"/>
          <m:FieldMap SourceField="Country" TargetField="@Country"/>
          <m:FieldMap SourceField="City" TargetField="@City"/>
        </m:Map>
      </m:Mappings>
    </m:MappingSchema>
    

    Xml-схема для Xml-представления ordersView:

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <xsd:element name="Order">
        <xsd:complexType>
          <xsd:attribute name="CustomerID" type="xsd:string"/>
          <xsd:attribute name="orderid" type="xsd:string"/>
          <xsd:attribute name="OrderDate" type="xsd:string"/>
          <xsd:attribute name="ShipCity" type="xsd:string"/>
          <xsd:attribute name="ShipDate" type="xsd:string"/>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
    

    Файл msd-схемы для Xml-представления ordersView:

    <m:MappingSchema
        xmlns:m="http://schemas.microsoft.com/data/2002/09/28/mapping">
      <m:DataSources>
        <m:DataSource Name="XML" Direction="Target" Type="Xml">
          <m:Schema Location="orders.xsd"/>
        </m:DataSource>
        <m:DataSource Name="Northwind" Direction="Source"
                      Type="SQL Server">
          <m:Schema Location="northwind.rsd"/>
          <m:Variable Name="Orders" Select="Orders"/>
        </m:DataSource>
      </m:DataSources>
      <m:Mappings>
        <m:Map SourceVariable="Orders" TargetSelect="/Order">
          <m:FieldMap SourceField="orderid" TargetField="@orderid"/>
          <m:FieldMap SourceField="CustomerID"
                      TargetField="@CustomerID"/>
          <m:FieldMap SourceField="OrderDate"
                      TargetField="@OrderDate"/>
          <m:FieldMap SourceField="ShipCity" TargetField="@ShipCity"/>
          <m:FieldMap SourceField="ShippedDate"
                      TargetField="@ShipDate"/>
        </m:Map>
      </m:Mappings>
    </m:MappingSchema>
    

    Код примера:

    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.Query;
    using System.Data.SqlXml;
    using System.Data.SqlClient;
    
    namespace SamplesXQery
    {
      class XQueryExample4
       {
          [STAThread]
          static void Main (string[] args)
          {
            MultipleXQuerySqlData();
          }
          
          public static void MultipleXQuerySqlData()
          {
            //SQL-соединение
            SqlConnection myConn =new SqlConnection("server="My Server";
            database=northwind;Integrated Security=SSPI");
            StreamWriter writer =new StreamWriter("output.xml");
    	   try
            {
    	     myConn.Open();
              //Инициализация XQueryProcessor 
    	     XQueryProcessor xq =new XQueryProcessor();
              //Добавление XML-представлений
    	     xq.XmlViewSchemaDictionary.Add("customersView", new 
              XmlTextReader("customers.msd"));
              xq.XmlViewSchemaDictionary.Add("ordersView", new 
              XmlTextReader("orders.msd"));
              //Добавление соединения с базой данных
              XmlDataSourceResolver datasource =new 
              XmlDataSourceResolver();
              datasource.Add("Northwind", myConn);
              //Запрос          
              string query = "declare namespace 
              map='http://schemas.microsoft.com/xml/2002/09/28/
              mappingfunctions'<root>{for $customer in 
              map:view('customersView')/Customer let $orders := 
              map:view('ordersView')/Order[@CustomerID=
              $customer/@CustomerID] return element Customer
              {$customer/@*, $orders}} </root>";
         
              xq.Compile(query);
    	     xq.Execute(datasource, writer);
            
    	   }
            catch (Exception e)
            {
              Console.WriteLine ("Exception: " + e.ToString ());
            }
            finally
            {
              writer.Close();
    	     myConn.Close();
            }
           
          }
       }
    }
    

    XQuery-запрос, используемый в примере, возвращает всех заказчиков (Customers) и соответствующие им заказы (Orders).

    Вы можете также создавать Xml-представления данных от нескольких подключений и даже данных, которые физически могут быть расположены на разных машинах. В следующем примере используется Xml-представление с именем productsView. Это представление также является подмножеством Xml-представления базы Northwind и отображает таблицу продуктов (Product). Xml-схема и msd-файл для Xml-представления productsView показаны ниже:

    Xml-схема для Xml-представления productsView:

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <xsd:element name="Product">
        <xsd:complexType>
          <xsd:attribute name="UnitQuantity" type="xsd:integer"/>
          <xsd:attribute name="ProductName" type="xsd:string"/>
          <xsd:attribute name="UnitPrice" type="xsd:decimal"/>
          <xsd:attribute name="QuantityPerUnit" type="xsd:string"/>
          <xsd:attribute name="ProductID" type="xsd:integer"/>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
    

    Файл msd-схемы для Xml-представления productsView:

    <m:MappingSchema
         xmlns:m="http://schemas.microsoft.com/data/2002/09/28/mapping">
      <m:DataSources>
        <m:DataSource Name="XML" Direction="Target" Type="Xml">
          <m:Schema Location="products.xsd"/>
        </m:DataSource>
        <m:DataSource Name="Northwind" Direction="Source"
                      Type="SQL Server">
          <m:Schema Location="northwind.rsd"/>
          <m:Variable Name="Products" Select="Products"/>
        </m:DataSource>
      </m:DataSources>
      <m:Mappings>
        <m:Map SourceVariable="Products" TargetSelect="/Product">
          <m:FieldMap SourceField="productname"
                      TargetField="@ProductName"/>
          <m:FieldMap SourceField="UnitPrice" TargetField="@UnitPrice"/>
          <m:FieldMap SourceField="QuantityPerUnit"
                      TargetField="@QuantityPerUnit"/>
          <m:FieldMap SourceField="ProductID" TargetField="@ProductID"/>
        </m:Map>
      </m:Mappings>
    </m:MappingSchema>
    

    Код примера подобен предыдущему за исключением того, что к XmlViewSchemaDictionary добавлено еще Xml-представление productsView. Два источника данных от разных подключений добавлены к XmlDatasourceResolver - источник данных Northwind, показанный в предыдущем примере и новый с именем productsDb.

    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.Query;
    using System.Data.SqlXml;
    using System.Data.SqlClient;
    
    namespace SamplesXQuery
    {
      class XQueryExample5
       {
          
          [STAThread]
          static void Main (string[] args)
          {
             MultiXQueryManySqlData();
          }
          
          public static void MultiXQueryManySqlData()
          {
            //Множественное SQL-соединение
            SqlConnection myConn =new SqlConnection("server="My  server";
            database=northwind;Integrated Security=SSPI");
            SqlConnection productsConn =new SqlConnection("server="My    
            server";database=northwind; Integrated Security=SSPI");
            StreamWriter writer =new StreamWriter("output.xml");
    	   try
            {
    	  
    	    myConn.Open();
    	    //Инициализация XQueryProcessor
             XQueryProcessor xq =new XQueryProcessor();
             //Добавление XML-представлений
    	    xq.XmlViewSchemaDictionary.Add("customersView", new 
             XmlTextReader("customers.msd"));
             xq.XmlViewSchemaDictionary.Add("ordersView", new 
             XmlTextReader("orders.msd"));
             xq.XmlViewSchemaDictionary.Add("productsView", new 
             XmlTextReader("products.msd"));
    	    //Добавление соединений с базами данных
             XmlDataSourceResolver datasource =new 
             XmlDataSourceResolver();
    	    datasource.Add("Northwind", myConn);
             datasource.Add("productsDb", productsConn);
             datasource.Add("orderDetails", "orderdetails.xml");
             //Запрос 
             string query = "declare namespace   
             map='http://schemas.microsoft.com/xml/2002/09/28/
             mappingfunctions'<root> {for $product in         
             map:view('productsView')/Product return element Product
             { $product/@*, for $orderdetails in 
             document('orderDetails')/details/orderdetail[@ProductID=
             $product/@ProductID] for $orders in  
             map:view('ordersView')/Order[@orderid=$orderdetails/@OrderID]  
             let $customers :=map:view('customersView')/Customer
             [@CustomerID=$orders/@CustomerID] return $customers}}
             </root>";
    	  
    	    xq.Compile(query);
    	    xq.Execute(datasource, writer);
            }
            catch (Exception e)
            {
               Console.WriteLine ("Exception: " + e.ToString ());
            }
            finally
            {
               writer.Close();
    	      myConn.Close();
            }
    	       
          }
       }
    }
    

    XQuery-запрос, используемый в этом примере, возвращает все элементы таблицы продуктов (Product). Дополнительно, в составе элементов Product, значение атрибута ProductID которых совпадает со значением атрибута ProductID элементов orderDetails (файл orderdetails.xml) выводятся заказчики (Customer), элементы заказов (Order) которых имеют значение атрибута OrderID совпадающее со значением одноименного атрибута элементов orderDetails.

    Приложение

    Текст файла books.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- This file represents a fragment of a bookstore database -->
    <bookstore>
      <book genre="autobiography" publicationdate="1981"
            ISBN="1-861-11-0">
        <title>The Autobiography of Benjamin Franklin</title>
        <author>
          <first-name>Benjamin</first-name>
          <last-name>Franklin</last-name>
        </author>
        <price>8.99</price>
      </book>
      <book genre="novel" publicationdate="1967" ISBN="0-201-63361-2">
        <title>The Confidence Man</title>
        <author>
          <first-name>Herman</first-name>
          <last-name>Melville</last-name>
        </author>
        <price>11.99</price>
      </book>
      <book genre="philosophy" publicationdate="1991"
            ISBN="1-861001-57-6">
        <title>The Gorgias</title>
        <author>
          <name>Plato</name>
        </author>
        <price>9.99</price>
      </book>
    </bookstore>
    
    Никакая часть настоящей статьи не может быть воспроизведена или передана в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, если на то нет письменного разрешения владельцев авторских прав.

    Материал, изложенный в данной статье, многократно проверен. Но, поскольку вероятность технических ошибок все равно существует, сообщество не может гарантировать абсолютную точность и правильность приводимых сведений. В связи с этим сообщество не несет ответственности за возможные ошибки, связанные с использованием статьи.
     
         

       
       
         
      VBNet рекомендует