Обработка баз данных на Visual Basic®.NET

Мак-Манус Джеффри П.

Голдштейн Джеки

Прайс Кевин Т.

ГЛАВА 10

ADO.NET и XML

 

 

Оставляя в стороне маркетинговую шумиху, следует отметить, что XML действительно способствует выполнению множества задач, связанных с программированием бизнес-решений. Это особенно верно в тех случаях, когда огромное значение приобретает интеграция разных систем и платформ в рамках одной или нескольких компаний. Поэтому при создании интегрированной среды разработки Visual Studio .NET одной из основных целей была расширенная поддержка XML.

Язык XML является форматом устойчивого хранения объекта DataSet, т.е. при сохранении на жестком диске объекта DataSet используется универсальный формат XML, а не двоичный или какой-то другой специализированный формат. Аналогично, при обмене объектами DataSet между разными процессами или компьютерами данные передаются в потоке формата XML.

В прежних главах показано, что объект DataSet не зависит от источника данных, которые он содержит. Для него данные — это всего лишь данные, независимо от их происхождения. То же самое верно, когда источник данных имеет формат XML. Объект DataSet обладает средствами чтения и записи данных в формате XML и/или информации о схеме данных. В модели ADO.NET эти средства обладают гораздо более высокими функциональными возможностями по сравнению с моделью ADO. Более того, благодаря объекту XmlDataDocument приложение может просматривать данные объекта DataSet и манипулировать ими с помощью реляционных инструментов и/или XML-инструментов, в зависимости от ситуации.

Полное описание XML и связанных с ним технологий и инструментов выходит за рамки данной книги. В главе 9, "XML и .NET", представлены основные сведения о расширенной поддержке XML на платформе.NET Framework, а в этой главе описываются основы интеграции ADO.NET и платформы .NET Framework. Более подробную информацию об этом можно найти в справочных материалах Visual Studio .NET и платформы .NET Framework, а также на Web-узле MSDN по адресу: http://msdn.microsoft.com.

 

Основные принципы чтения и записи XML-данных

 

В главах 5, "ADO.NET: объект DataSet", и 6, "ADO.NET: объект DataAdapter" демонстрируются программируемые и прямые способы загрузки данных в объект DataSet из базы данных. Еще один метод загрузки данных основан на чтении XML-данных. Как и следовало ожидать, в объект DataSet данные можно записывать в XML-формате. Более того, объект DataSet обладает средствами чтения и записи XML-схем либо вместе с XML-данными, либо раздельно.

 

Чтение XML-данных

В ADO.NET предусмотрены богатые и разнообразные инструменты чтения и записи XML-данных и XML-схем. Далее рассматриваются основные способы использования предназначенных для этого методов и свойств.

Как и в предыдущих главах, для демонстрации взаимодействия модели ADO.NET и XML здесь приводится практический пример их использования на основе простой формы.

1. Запустите интегрированную среду разработки Visual Studio .NET и создайте новый проект Visual Basic Windows Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов), а затем шаблон Windows Application (Приложение Windows) в области Templates (Шаблоны).

2. Назовите проект ADO-XML.

3. Укажите путь к файлам проекта.

4. Увеличьте размер формы Form1.

5. В окне свойств Properties укажите значение frmXML для свойства (Name) и значение ADO.NET and XML для свойства Text формы Form1.

6. В верхнем левом углу формы создайте кнопку, перетаскивая ее из панели элементов управления.

7. В окне свойств Properties укажите значение btnReadXML для свойства (Name) и значение Read XML для свойства Text этой кнопки.

8. В правой части формы создайте сетку DataGrid, перетаскивая ее из панели элементов управления.

9. В окне свойств Properties укажите значение grdData для свойства (Name) сетки.

10. Увеличьте размер сетки grdData, чтобы она занимала до 80% всей площади формы.

В верхней части файла введите следующий код:

Imports System

Imports System.Data

Imports System.Data.SqlClient

Затем в определении класса формы frmXML введите код из листинга 10.1.

Листинг 10.1. Код чтения содержимого XML-файла в объект DataSet

Private Sub btnReadXML_Click(ByVal sender As System.Object, _

 ByVal e As System.EventArgs) Handles btnReadXML.Click

 Dim dsPubs As New DataSet()

 ' Чтение XML-данных из файла.

 dsPubs.ReadXml("..\Pubs.xml")

 ' Связывание объекта DataSet с сеткой данных DataGrid.

 grdData.DataMember = "publishers"

 grdData.DataSource = dsPubs

End Sub

Эта подпрограмма считывает XML-данные из файла pubs.xml в объект DataSet. Теперь объект DataSet и его данные могут использоваться любым из способов, описанных в предыдущих главах. Кроме того, эта подпрограмма связывает объект DataSet с сеткой данных DataGrid. В листинге 10.2 показано содержимое файла pubs.xml, а на рис. 10.1 — данные в сетке DataGrid.

РИС. 10.1. Содержимое файла pubs.xml в сетке DataGrid

Листинг 10.2. Содержимое файла pubs.

 

  0736

  New Moon Books

  Boston

  MA

  USA

 

 

  0877

  Binnet & Hardley

  Washington

  DC

  USA

 

 

  1389

  Algodata Infosystems

  Berkeley

  CA

  USA

 

 

  1622

  Five Lakes Publishing

  Chicago

  IL

  USA

 

 

  1756

  Ramona Publishers

  Dallas

  TX

  USA

 

 

  9952

  Scootney Books

  New York

  NY

  USA

 

 

  9999

  Lucerne Publishing

  France

 

НА ЗАМЕТКУ

После вызова метода ReadXML для загрузки данных в объект DataSet для свойства RowState всех новых строк задается значение Added. Этот подход отличается от принятого по умолчанию поведения, когда для загрузки данных в объект DataSet из базы данных используется объект DataAdapter и для свойства RowState всех новых строк задается значение Unchanged. Такой подход позволяет загружать данные из XML-источника и вставлять их в таблицу базы данных. Если вы не хотите этого делать, то можно с помощью метода AcceptChanges переустановить значение Unchanged для свойства RowState всех новых строк. Для изменения принятого по умолчанию поведения при загрузке данных в объект DataSet из базы данных можно указать значение False для свойства АсcеptChangesOnFill что приведет к автоматической установке значения Added для свойства RowState всех новых строк.

В данном примере демонстрируется простейший способ чтения XML-данных в объект DataSet, т.е. чтение из файла. Помимо этого способа, существует много других вариантов чтения XML-данных с помощью перегруженных версий метода ReadXML, например с помощью объектов Stream, TextReader или XmlReader. Соответствующие им перегруженные версии метода ReadXML содержат второй параметр со значением XmlReadMode. Этот параметр используется для указания способа интерпретации содержимого XML-источника и обработки схемы данных. В табл. 10.1 приведено краткое описание членов перечисления XmlReadMode.

Таблица 10.1. Перечисление XmlReadMode 

Имя члена перечисления Описание
ReadSchema Считывает любую встроенную схему и загружает ее вместе с данными в объект DataSet. Таблицы, определенные в схеме, вставляются в объект Data-Set, но если схема определяет таблицу, которая уже есть в этом наборе данных, то генерируется исключительная ситуация
IgnoreSchema Игнорирует любую встроенную схему и загружает данные в объект DataSet с помощью существующего определения схемы. Любые данные, которые не соответствуют схеме объекта DataSet, игнорируются и не загружаются. Аналогично, если схема не определена, то данные не загружаются
InferSchema Игнорирует любую встроенную схему и выводит схему на основе структуры данных, а затем загружает данные в объект DataSet. Дополнительные таблицы и поля, определенные в результате такого анализа структуры данных, добавляются в уже существующую схему в объекте DataSet. В случае конфликта определений генерируется исключительная ситуация
Fragment Считывает все существующие XML-фрагменты и загружает данные в объект DataSet. Любые данные, которые не соответствуют схеме в объекте DataSet, игнорируются и не загружаются
DiffGram Считывает данные в формате DiffGram и загружает данные в объект DataSet. Новые записи сливаются с уже существующими записями с такими же значениями уникального идентификатора, в противном случае в объекте DataSet создаются новые записи. В случае несоответствия схем генерируется исключительная ситуация. (Формат DiffGram более подробно описывается далее в главе. — Прим. ред. )
Auto Этот режим используется по умолчанию. При этом выполняется одна из следующих операций: если XML-данные имеют формат DiffGram, то выбирается член DiffGram перечисления XmlReadMode; если схема определена в объекте DataSet или встроена в XML-документ, то выбирается член ReadSchema перечисления XmlReadMode; в остальных случаях выбирается член InferSchema перечисления XmlReadMode  

Для чтения только схемы данных (и игнорирования данных) предусмотрена перегруженная версия метода ReadXmlSchema, которая может применяться для считывания схемы объектов DataTable объекта DataSet так, как показано ниже. MyDataSet.ReadXmlSchema("MySchemaFile.xml")

Те же четыре источника данных (файл, объекты Stream, TextReader и XmlReader) могут использоваться вместе с методом ReadXmlSchema. Объект DataSet имеет аналогичный набор методов для записи XML-данных.

 

Запись XML-данных

После загрузки в объект DataSet (независимо от способа и места загрузки) данные и/или схемы данных можно записать в XML-формате (с XML-схемой или без нее). Для демонстрации способов записи данных в XML-формате выполните перечисленные ниже действия.

1. Создайте в форме новую кнопку сразу под кнопкой Read XML, перетаскивая ее из панели инструментов. 

2. В окне свойств Properties укажите значение btnWriteXML для свойства (Name) и значение Write XML для свойства Text этой кнопки.

3. Затем в определении класса формы frmXML введите код из листинга 10.3.

Листинг 10.3. Код сохранения содержимого объекта DataSet в виде XML-файла

Private Sub btnWriteXML_Click(ByVal sender As System.Object, _

 ByVal e As System.EventArgs) Handles btnWriteXML.Click

 Dim dsSales As New DataSet()

 Dim en As New SqlConnection _

  ("data source=localhost;initial catalog=pubs;user id=sa")

 Dim daAuthors As New SqlDataAdapter("select * from sales", en)

 Dim daPublishers As New SqlDataAdapter("select * from stores", en)

 ' Загрузка реляционных данных из базы данных.

 daAuthors.Fill(dsSales, "Sales")

 daPublishers.Fill(dsSales, "Stores")

 ' Запись XML-данных в файл

 dsSales.WriteXml("…\StoreSales.xml")

 ' Запись схемы в XSD-файл.

 dsSales.WriteXmlSchema("…\StoreSales.xsd")

End Sub

В этой подпрограмме создаются два объекта — адаптера данных (daAuthors и daPublishers), которые затем используются для вставки данных в объект dsPubs из двух таблиц базы данных pubs СУБД SQL Server. В листинге 10.4 приведено содержимое файла StoreSales.xml, который создается в результате выполнения этой подпрограммы. Обратите внимание на то, что данный XML-документ содержит записи о продажах, а затем записи о магазинах. Этот подход имеет смысл, так как между ними не задано никакого отношения. Если бы таблицы Sales и Stores были связаны, то эти записи можно было вложить друг в друга. Пример такого вложения приводится далее, в бизнес-ситуации 10.1.

ЛИСТИНГ 10.4. Содержимое файла StoreSales.xml

 

  6380

  6871

  l994-09-14T00:00:00.0000000+02:00

  5

  Net 60

  BU1032

 

 

  6380

  722a

  l994-09-13T00:00:00.0000000+02:00

  3

  Net 60

  PS2091

 

 

  7066

  A2976

  1993-05-24T00:00:00.0000000+02:00

  50

  Net 30

  PC8888

 

 

  7066

  QA7442.3

  1994-09-13T00:00:00.0000000+02:00

  75

  ON invoice

  PS209K/title_id>

 

 

  7067

  D4482

  1994-09-14T00:00:00.0000000+02:00

  10

  Net 60

  PS2091

 

 

  7067

  P2121

  1992-06-15T00:00:00.0000000+02:00

  40

  Net 30

  TC3218

  7067

  <оrd_num>P2121

  1992-06-15T00:00:00.0000000+02:00

  20

  Net 30

  TC4203

 

 

  7067

  P2121

  1992-06-15T00:00:00.0000000+02:00

  20

  Net 30

  TC7777

 

 

  7131

  N914008

  1994-09-14T00:00:00.0000000+02:00

  20

  Net 30

  PS2091

  7131

  <оrd_num>N914014

  1994-09-14T00:00:00.0000000+02:00

  25

  Net 30

  MC3021

 

 

  7131

  P3087a

  1993-05-29T00:00:00.0000000+02:00

  20

  Net 60

  PS1372

 

 

  7131

  P3087a

  1993-05-29T00:00:00.0000000+02:00

  25

  Net 60

  PS2106

  7131

  P3087a

  1993-05-29T00:00:00.0000000+02:00

  15

  Net 60

  PS3333

 

 

  7131

  P3087a

  1993-05-29T00:00:00.0000000+02:00

  25

  Net 60

  PS7777

 

 

  7896

  QQ2299

  1993-10-28T00:00:00.0000000+02:00

  15

  Net 60

  BU7832

 

 

  7896

  TQ456

  1993-12-12T00:00:00.0000000+02:00

  10

  Net 60

  MC2222

 

 

  7896

  X999

  1993-02-21T00:00:00.0000000+02:00

  35

  ON invoice

  BU2075

 

 

  8042

  423LL922

  1994-09-14T00:00:00.0000000+02:00

  15

  ON invoice

  MC3021

  8042

  423LL930

  1994-09-14T00:00:00.0000000+02:00

  10

  ON invoice

  BU1032

 

 

  8042

  P723

  1993-03-11T00:00:00.0000000+02:00

  25

  Net 30

  BU1111

 

 

  8042

  QA879.1

  1993-05-22T00:00:00.0000000+02:00

  30

  Net 30

  PC1035

 

 

  6380

  Eric the Read Books

  788 Catamaugus Ave.

  Seattle

  WA

  98056

 

 

  7066

  Barnum's

  567 Pasadena Ave.

  CA

  92789

 

 

  7067

  News & Brews

  577 First St.

  Los Gatos

  CA

  96745

 

 

  7131

  Doc-U-Mat: Quality Laundry and Books

  24-A Avogadro Way

  Remulade

  WA

  98014

 

 

  7896

  Fricative Bookshop

  89 Madison St.

  Fremont

  CA

  90019

 

 

  8042

  Bookbeat

  679 Carson St.

  Portland

  OR

  89076

 

В приведенном примере демонстрируется простейший способ записи данных из объекта DataSet в XML-файл. Помимо этого способа существуют другие варианты записи данных и схемы с помощью перегруженных версий метода WriteXML. Соответствующие им перегруженные версии метода ReadXML содержат второй параметр со значением XmlWriteMode. В табл. 10.2 приведено краткое описание членов перечисления XmlWriteMode.

Таблица 10.2. Перечисление XmlWriteMode 

Имя члена перечисления Описание
DiffGram Записывает данные из объекта DataSet в виде объекта DiffGram с текущими исходными значениями всех записей
WriteSchema Записывает данные из объекта DataSet в XML-формате вместе с их схемой. Если объект DataSet содержит только схему, то записывается только схема. Если в объекте DataSet не определена схема, то ничего не записывается
IgnoreSchema Этот режим используется по умолчанию для записи содержимого объекта DataSet в XML-формате без схемы  

НА ЗАМЕТКУ

Объект DataSet имеет методGetXml, который возвращает XML-строку с данными из объекта DataSet. Точно такой же результат можно получить, используя метод со вторым параметром XmlWriteMode, который имеет значение IgnoreSchema. Извлечение данных в виде одной строки иногда позволяет более гибко программировать доступ к данным, но требует больших усилий, особенно если вам нужно всего лишь записать данные в файл. 

Для записи схемы данных из объекта DataSet в отдельном XSD-файле (вместо встраивания ее в данные) предусмотрен метод WriteXmlSchema, который используется так, как показано ниже.

dsSales.WriteXmlSchema

В листинге 10.5 приведено содержимое файла StoreSales.xsd, полученного в результате выполнения этого метода.

Листинг 10.5. Содержимое файла StoreSales.xsd, который является схемой из объекта dsSales

 

  

  

    

     

     

      

      

      

      

      

      

     

    

   

   

    

     

      

       

      

      

      

      

     

     

    

   

  

 

 

Формат DiffCram

Рассмотрим теперь формат DiffGram, который используется членами перечислений XmlReadMode и XmlWriteMode. Это XML-формат, содержащий не только текущие значения элементов-данных, но и их исходные значения, которые были изменены или удалены (вслед за последним вызовом метода AcceptChanges). Итак, DiffGram представляет собой формат сериализации, который используется для передачи данных другому процессу или компьютеру. Поскольку эти данные фактически имеют XML-формат, они могут легко передаваться другим платформам, например UNIX или Linux.

Каждый файл формата DiffGram имеет три раздела. Первый раздел содержит текущие значения всех записей объекта DataSet, независимо от того, изменялись они или нет. Любой измененный элемент (запись) обозначается как diffgr:hasChanges="modified", а любой добавленный элемент (запись) — как diffgr:hasChanges="inserted". Второй раздел содержит исходные значения всех измененных и удаленных записей. Его элементы связаны с соответствующими элементами первого раздела, которые обозначаются как diffgr:id="xxx", где ххх — идентификатор записи. Третий раздел содержит информацию об ошибках работы с записями. Его элементы связаны аналогичным образом с соответствующими элементами первого раздела, которые обозначаются как diffgr:id="xxx".

Для генерации XML-файла в формате DiffGram нужно внести приведенные ниже изменения (они обозначены полужирным начертанием) в подпрограмму btnWriteXML_Click.H3 листинга 10.3.

Private Sub btnWriteXML_Click(ByVal sender As System.Object, _

 ByVal e As System.EventArgs) Handles btnWriteXML.Click

 Dim dsSales As New DataSet()

 Dim en As New SqlConnection _

  ("data source=localhost;initial catalog=pubs;user id=sa")

 Dim daAuthors As New SqlDataAdapter("select * from sales ", en)

 Dim daPublishers As New SqlDataAdapter("select * from stores ", en)

 ' Загрузка реляционных данных из базы данных.

 daAuthors.Fill(dsSales, "Sales")

 daPublishers.Fill(dsSales, "Stores")

 ' Запись XML-данных в файл.

 dsSales.WriteXml("..\StoreSales.xml")

 ' Запись схемы в XSD-файл.

 dsSales.WriteXmlSchema("..\StoreSales.xsd")

 ' Пример изменения, удаления и вставки новой записи.

 dsSales.Tables("Stores").Rows(0)("stor_id") = 999 ' Изменение

 dsSales.Tables("Stores").Rows(1).Delete() ' Удаление

 Dim rr As DataRow = dsSales.Tables("Stores").NewRow()

 rr("stor_name") = "New Store"

 dsSales.Tables("Stores").Rows.Add(rr) ' Вставка

 ' Сохранение только измененных записей в формате DiffGram.

 Dim ChangedDataSet = dsSales.GetChanges()

 ChangedDataSet.WriteXml("…\Changes.xml", XmlWriteMode.DiffGram)

 ' Сохранение всех записей в формате DiffGram.

 dsSales.WriteXml("…\DiffGram.xml", XmlWriteMode.DiffGram)

End Sub

В листинге 10.6 приведено содержимое файла DiffGram.xml, полученного в результате щелчка на кнопке Write XML формы проекта ADO-XML. Внесенные в таблицу Stores изменения выделены полужирным начертанием. Удаленная запись отсутствует в разделе текущих данных, но присутствует в разделе исходного состояния вместе с исходным значением измененной записи. Кроме того, раздел с текущими данными содержит новую запись с отметкой "inserted" (вставлена).

Листинг 10.6. XML-файл DiffGram.xml в формате DiffGram с одной вставленной записью, одной удаленной записью и одной измененной записью

 

 

   6380

   6871

   1994-09-14T00:00:00.0000000+02:00

   5

   Net 60

   BU1032

 

  

   6380

   722a

   1994-09-13T00:00:00.0000000+02:00

   3

   Net 60

   PS2091

  

  

   7066

   A2976

   1993-05-24T00:00:00.0000000+02:00

   50

   Net 30

   PC8888

  

  

   7066

   QA7442.3

   1994-09-13T00:00:00.0000000+02:00

   75

   ON invoice

   PS2091

  

  

   7067

   D4482

   1994-09-14T00:00:00.0000000+02:00

   10

   Net 60

   PS2091

  

  

   7067

   P2121

   1992-06-15T00:00:00.0000000+02:00

   40

   Net 30

   TC3218

  

 

   7067

   P2121

   1992-06-15T00:00:00.0000000+02:00

   20

   Net 30

   TC4203

  

  

   7067

   P2121

   1992-06-15T00:00:00.0000000+02:00

   20

   Net 30

   TC7777

  

  

   713K/stor_id>

   N914008

   1994-09-14T00:00:00.0000000+02:00

   20

   Net 30

   PS2091

  

  

   7131

   N914014

   1994-09-14T00:00:00.0000000+02:00

   25

   Net 30

   MC3021

 

  

   7131

   P3087a

   1993-05-29T00:00:00.0000000+02:00

   20

   Net 60

   PS1372

  

 

   7131

   P3087a

   1993-05-29T00:00:00.0000000+02:00

   25

   Net 60

   PS2106

  

  

   7131

   P3087a

   1993-05-29T00:00:00.0000000+02:00

   15

   Net 60

   PS3333

  

  

   7131

   P3087a

   1993-05-29T00:00:00.0000000+02:00

   25

   Net 60

   PS7777

  

 

   7896

   QQ2299

   1993-10-28T00:00:00.0000000+02:00

   15

   Net 60

   BU7832

  

  

   7896

   TQ456

   1993-12-12T00:00:00.0000000+02:00

   10

   Net 60

   MC2222

  

  

   7896

   X999

   1993-02-21T00:00:00.0000000+02:00

   35

   ON invoice

   BU2075

  

  

   8042

   423LL922

   1994-09-14T00:00:00.0000000+02:00

   15

   ON invoice

   MC3021

  

  

   8042

   <оrd_num>423LL930

   1994-09-14T00:00:00.0000000+02:00

   10

   ON invoice

   BU1032

  

  

   8042

   P723

   1993-03-11T00:00:00.0000000+02:00

   25

   Net 30

   BU1111

  

  

   8042

   QA879.1

   1993-05-22T00:00:00.0000000+02:00

   30

   Net 30

   PC1035

 

  

   999

   Eric the Read Books

   788 Catamaugus Ave.

   Seattle

   WA

   98056

  

  

   7067

   News & Brews

   577 First St.

   Los Gatos

   CA

   96745

  

  

   7131

   Doc-U-Mat: Quality Laundry and Books

   24-A Avogadro Way

   Remulade

   WA

   98014

  

  

   7896

   Fricative Bookshop

   89 Madison St.

   Fremont

   CA

   90019

  

  

   8042

   Bookbeat

   679 Carson St.

   Portland

   OR

   89076

  

 

   New Store

 

 

 

 

   6380

   Eric the Read Books

   788 Catamaugus Ave.

   Seattle

   WA

   98056

  

  

   7066

   Barnum's

   567 Pasadena Ave.

   Tustin

   CA

   92789

 

 

НА ЗАМЕТКУ

Для сохранения только измененных записей в формате DiffGram следует использовать метод GetChanges:

Dim ChangedDataSet = dsSales.GetChanges()

ChangedDataSet.WriteXml("..\Changes.xml", XmlWriteMode.DiffGram)

В листинге 10.7 приведено содержимое файла DiffGram.xml, полученного в pезультате выполнения этого метода. 

Листинг 10.7. XML-файл changes.xml в формате DiffGram только с измененными записями

 

 

   999

   Eric the Read Books

   788 Catamaugus Ave.

   Seattle

   WA

   98056

  

 

   New Store

  

 

 

 

   6380

   Eric the Read Books

   788 Catamaugus Ave.

   Seattle

   WA

   98056

  

 

   7066

   Barnum's

   567 Pasadena Ave.

   Tustin

   CA

   92789

 

 

 

Бизнес-ситуация 10.1: подготовка XML-файлов для бизнес-партнеров

Компания Jones Novelty обменивается информацией с помощью электронных средств передачи данных со многими своими поставщиками и партнерами. Развитие этой тенденции, вероятно, приведет к тому, что компании придется спустя какое-то время применить качественно новое решение, например систему Microsoft BizTalk Server. В настоящее время Брэд Джонс стремится удовлетворить текущие потребности и сохранить конкурентоспособность, применяя XML для передачи транзакций. Для этого будут использованы уже описанные возможности XML и ряд других, которые описываются далее. Следует отметить, что многие требования можно удовлетворить даже без использования "мощных и тяжеловесных" платформ, инструментов и технологий, например XSLT.

Сначала следует организовать отправку XML-файла с перечнем товаров на складах. Для этого должны быть посланы сведения из всех полей таблицы tblInventory, за исключением поля WholesalePrice. Хотя это можно организовать с помощью простого запроса, который включает все поля, кроме WholesalePrice, в данном примере используется способ на основе XML-свойств. Для создаваемого XML-файл существует еще одно требование: в него необходимо включить XSD-схему с описанием всех полей как элементов, за исключением поля ID, которое передается с помощью атрибута.

Для создания этого приложения выполните перечисленные ниже действия.

1. Запустите интегрированную среду разработки Visual Studio .NET и создайте новый проект Visual Basic Windows Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов), а затем шаблон Windows Application (Приложение Windows) в области Templates (Шаблоны).

2. Назовите проект BusinessCaseIO.

3. Укажите путь к файлам проекта.

4. Увеличьте размер формы Form1.

5. В окне свойств Properties укажите значение frmPrepareXML для свойства (Name) и значение Prepare XML для свойства Text формы Form1.

6. В верхнем левом углу формы создайте кнопку, перетаскивая ее из панели элементов управления.

7. В окне свойств Properties укажите значение btnInventory для свойства (Name) и значение Create Inventory XML для свойства Text этой кнопки.

В верхней части файла введите следующий код:

Imports System

Imports System.Data

Imports System.Data.SqlClient

Затем в определении класса формы frmPrepareXML введите приведенный ниже код.

Dim en As New SqlConnection _

 ("data source=localhost;initial catalog=Novelty;user id=sa")

Private Sub btnInventory_Click(ByVal sender As System.Object, _

 ByVal e As System.EventArgs) Handles btnlnventory.Click

 Dim dsInventory As New DataSet()

 Dim daInventory As New SqlDataAdapter _

  ("select * from tblInventory ", en)

 daInventory.Fill(dsInventory, "tblInventory")

 ' Сохранение поля ID как XML-атрибута, а не элемента

 dsInventory.Tables("tblInventory").Columns("ID").ColumnMapping = _

  MappingType.Attribute

 ' Сокрытие поля WholesalePrice в сохраненном XML-файле

 dslnventory.Tables("tbllnventory").Columns _

  ("WholesalePrice").ColumnMapping = MappingType.Hidden

 ' Сохранение данных в XML-файле, включая встроенную схему.

 dsInventory.WriteXml("..\Inventory.xml", XmlWriteMode.WriteSchema)

End Sub

После вставки данных в объект DataSet для формирования XML-файла используются следующие два выражение. Первое выражение указывает на то, что поле ID следует сохранить как XML-атрибут:

dsInventory.Tables("tblInventory").Columns("ID").ColumnMapping = _

 MappingType.Attribute

Второе выражение указывает на сокрытие поля WholesalePrice в сохраненном XML-файле:

dslnventory.Tables("tbllnventory").Columns _

 ("WholesalePrice").ColumnMapping = MappingType.Hidden

Наконец, во время сохранения данных используется второй параметр метода WriteXML, который указывает на необходимость включения XSD-схемы вместе с данными. Полученный в результате XML-файл показан в листинге 10.8.

Листинг 10.8. Пример сохранения таблицы tblInventory в виде XML-файла

 

 

  

   

     

      

       

       

        

       

       

      

      

      

    

    

  

  

 

 

  Rubber Chicken

  2.99

  The quintessential rubber chicken.

  

  

   Joy Buzzer

   9.99

   They will get a real shock out of this.

  

  

   Seltzer Bottle

   15.24

   Seltzer sold separately.

  

 

   Ant Farm

   14.99

   Watch ants where they live and breed.

  

  

   Wind-Up Robot

   29.99

   Giant robot: attack toybox!

  

  

   Rubber Eyeballs

   0.99

   Peek-a-boo!

  

  

   Doggy Mess

   1.99

   Yechhh!

 

 

  Mini-Camera

  9.99

  For future spies!

 

 

  Glow Worms

  1.99

  Makes them easy to find

 

 

  Insect Pops

  0.99

  Special treats

 

 

  Alien Alarm Clock

  45.99

  Do you know what time it is out there?

  

  

   Cinnamon Toothpicks

   1.99

   Really wakes up your mouth

 

 

Для составления ведомости на выдачу заработной платы сотрудникам компании нужно создать код сохранения информации о сотрудниках в формате XML для каждого отдела. Для этого разработчик базы данных должен включить в форму frmPrepareXML вторую кнопку btnEmployees и вставить в код класса формы frmPrepareXML код из листинга 10.9.

Листинг 10.9. Код сохранения данных из таблиц tblEmployee и tblDepartment в XML-файле

Private Sub btnEmployees_Click (ByVal sender As System.Object, _

 ByVal e As System.EventArgs) Handles btnEmployees.Click

 Dim dsEmployees As New DataSet()

 Dim daEmployees As New SqlDataAdapter _

  ("select * from tblEmployee", en)

 Dim daDepartments As New SqlDataAdapter _

  ("select * from tblDepartment", en)

 daDepartments.Fill(dsEmployees,"tblDepartment")

 daEmployees.Fill(dsEmployees, "tblEmployee")

 ' Определение отношения между таблицами.

 dsEmployees.Relations.Add("DepartmentEmployees", _

  dsEmployees.Tables("tblDepartment").Columns("ID"), _

  dsEmployees.Tables("tblEmployee").Columns("DepartmentID"))

 ' Сохранение данных в XML-файле.

 dsEmployees.WriteXml("..\Employees.xml")

End Sub

В этом коде для сохранения данных из таблиц tblDepartment и tblEmployee в XML-файле используются предлагаемые по умолчанию параметры объекта DataSet. Полученный в результате XML-файл Employees.xml показан в листинге 10.10.

Листинг 10.10. XML-файл Employees. xml, полученный в результате сохранения данных из таблиц tblDepartment и tblEmployee

 

  1

  Administration

 

 

  2

  Engineering

 

 

  3

  Sales

 

 

  4

  Marketing

 

 

  1

  Carole

  Vermeren

  2

  222

 

 

  2

  Cathy

  Johnson

  2

  13000

 

 

  3

  Eric

  Haglund

  4

  12000

 

 

  4

  Julie

  Ryan

  1

  4000

 

 

  5

  Richard

  Halpin

  2

  10000

 

 

  6

  Kathleen

  Johnson

  3

  18000

 

 

  7

  Sorel

  Polito

  4

  28000

  

 

  8

  Sorel

  Terman

  1

  8000

 

 

  9

  Randy

  Hobaica

  2

  18000

 

 

  10

  Matthew

  Haglund

  3

  30000

 

 

  11

  Cathy

  Vermeren

  4

  0

 

 

  12

  Brad

  Townsend

  2

  12000

 

 

  13

  Jennifer

  Eves

  2

  26000

 

 

  14

  Steve

  Marshall

  3

  42000

 

 

  15

  Laura

  Davidson

  4

  60000

 

 

  16

  Angela

  Stefanac

  2

  16000

 

 

  17

  Marjorie

  Bassett

  2

  34000

 

 

  18

  Joe

  Chideya

  3

  54000

 

 

  19

  Katie

  Chideya

  4

  76000

 

 

  20

  Terri

  Allen

  1

  20000

 

 

  21

  Mike

  Doberstein

  2

  42000

 

 

  22

  Terri

  Woodruff

  3

  66000

 

 

  23

  Cathy

  Rosenthal

  4

  0

 

 

  24

  Margaret

  Eves

  1

  24000

 

 

  25

  Mikki

  Lemay

  2

  50000

 

 

  26

  Randy

  Nelson

  3

  78000

 

 

  27

  Kathleen

  Husbands

  4

  108000

 

 

  28

  Kathleen

  Eberman

  1

  28000

 

 

  29

  Richard

  Rosenthal

  2

  58000

 

 

  30

  Mike

  Woodruff

  3

  90000

 

К сожалению, данный XML-файл содержит список сотрудников, который не сгруппирован по отделам. Несмотря на созданное отношение между родительской таблицей tblDepartment и дочерней таблицей tblEmployee полученный XML-файл содержит данные отдельно для каждой таблицы. Для вложения дочерних элементов из таблицы tblEmployee в родительские элементы из таблицы tblDepartment нужно указать значение True для свойства Nested объекта-отношения Relation.

dsEmployees.Relations("DepartmentEmployees").Nested = True

После вставки этой строки кода перед строкой сохранения данных

dsInventory.WriteXml("..\Inventory.xml", XmlWriteMode.WriteSchema)

будет получен XML-файл Employees.xml, показанный в листинге 10.11.

 Листинг 10.11. XML-файл Employees.xml с вложением дочерних элементов из таблицы tblEmployee в родительские элементы из таблицы tblDepartment

 

  1

  Administration

 

   2035

   Julie

   Ryan

   1

   4000

  

  

   2039

   Sorel

   Terman

   1

   8000

  

  

   2051

   Terri

   Allen

   1

   20000

  

  

   2055

   Margaret

   Eves

   1

   24000

  

  

   2059

   Kathleen

   Eberman

   1

   28000

  

 

 

  2

  Engineering

  

   2032

   Carole

   Vermeren

   2

   222

  

  

   2033

   Cathy

   Johnson

   2

   13000

 

 

   2036

   Richard

   Halpin

   2

   10000

 

 

   2040

   Randy

   Hobaica

   2

   18000

 

 

   2043

   Brad

   Townsend

   2

   12000

 

 

   2044

   Jennifer

   Eves

   2

   26000

 

 

   204V

   Angela

   Stefanac

   2

   16000

 

 

   2048

   Marjorie

   Bassett

   2

   34000

 

 

   2052

   Mike

   Doberstein

   2

   42000

 

 

   2056

   Mikki

   Lemay

   2

   50000

 

 

   2060

   Richard

   Rosenthal

   2

   58000

 

 

 

  3

  Sales

 

   2037

   Kathleen

   Johnson

   3

   18000

 

 

   2041

   Matthew

   Haglund

   3

   30000

 

 

   2045

   Steve

   Marshall

   3

   42000

 

 

   2049

   Joe

   Chideya

   3

   54000

 

  

   2053

   Terri

   Woodruff

   3

   66000

 

 

   2057

   Randy

   Nelson

   3

   78000

 

 

   2061

   Mike

   Woodruff

   3

   90000

 

 

 

  4

  Marketing

  

   2034

   Eric

   Haglund

   4

   12000

  

  

   2038

   Sorel

   Polito

   4

   28000

  

  

   2042

   Cathy

   Vermeren

   4

   0

  

  

   2046

   Laura

   Davidson

   4

   60000

  

  

   2050

   Katie

   Chideya

   4

   76000

  

 

   2054

   Cathy

   Rosenthal

   4

   5555

  

  

   2058

   Kathleen

   Husbands

   4

   108000

 

 

 

Создание объекта XmlReader с помощью объекта Command

В главе 4, "Модель ADO.NET: провайдеры данных", описываются способы работы с объектом Command, который является ключевым объектом — провайдером данных на платформе .NET. В ней рассматриваются способы выполнения команд на основе объектов ExecuteReader, ExecuteScalar и ExecuteNonQuery. Хотя все провайдеры данных на платформе .NET реализуют эти методы, объект SqlCommand имеет дополнительный метод ExecuteXmlReader, который используется для извлечения и доступа к XML-данным из СУБД SQL Server.

Метод ExecuteXmlReader возвращает объект XmlReader точно так же, как он возвращает объект DataReader.

Для демонстрации применения метода ExecuteXmlReader вернитесь к проекту ADO-XML и выполните следующее.

1. Включите в форму frmXML под кнопкой Write XML еще одну кнопку, перетаскивая ее из панели элементов управления.

2. В окне свойств Properties укажите значение btnExecuteXML для свойства (Name) и значение ExecuteXMLReader для свойства Text этой кнопки.

3. Затем в определении класса формы frmXML введите код из листинга 10.12.

Листинг 10.12. Код извлечения и обработки данных из СУБД SQL Server в формате XML

Private Sub btnExecuteXML_Click(ByVal sender As System.Object, _

 ByVal e As System.EventArgs) Handles btnExecuteXML.Click

 Dim cn As New SqlConnection _

  ("data source=localhost;initial catalog=pubs;user id=sa")

 Dim cmd As New SqlCommand _

  ("select * from stores for xml auto, elements", en)

 Dim reader As Xml.XmlReader

 Dim str As New System.Text.StringBuilder()

 cn.Open()

 ' Выполнение SQL-команды Select с предложением FOR XML.

 reader = cmd.ExecuteXmlReader()

 ' Поиск и извлечение данных из узлов-элементов.

 While reader.Read()

  Select Case reader.NodeType

  Case Xml.XmlNodeType.Element

   str.Append("<" & reader.Name & ">")

  Case Xml.XmlNodeType.EndElement

   str.Append("" & ControlChars.CrLf)

  Case Xml.XmlNodeType.Text

   str.Append(reader.Value)

  Case Else

   ' В данном примере игнорируется.

  End Select

 End While

 MsgBox(str.ToString)

 сn.Close()

End Sub

Код в листинге 10.12 содержит пример упрощенного использования метода ЕхеcuteXmlReader в котором просто отображаются данные (вместе с дескрипторами) из таблицы базы данных pubs. СУБД SQL Server передается показанная ниже SQL-команда Select, в которой явно указаны возвращаемые поля в виде XML-элементов.

"select * from stores for xml auto, elements"

В таком случае обработка разных типов XML-узлов упрощается, потому что достаточно найти только начальный и конечный узлы, а текстовые узлы между ними будут содержать фактические данные. Более надежная обработка XML-документа основана на конструкции Select Case, в которой учтены узлы всех типов. После щелчка на кнопке ExecuteXMLReader будет выполнен код из листинга 10.12 и на экране появится диалоговое окно, которое показано на рис. 10.2.

РИС. 10.2. Диалоговое окно с XML-данными, извлеченными из СУБД SQL Server

 

Объект XmlDataDocument

В главе 9, "XML и .NET", рассматривается объект XmlDataDocument и способы его использования для доступа к иерархическим данным в виде узлов загруженного в оперативную память XML-документа. В этой книге также рассматриваются способы извлечения реляционных данных (и доступа к ним) из традиционной SQL-совеместимой базы данных. При этом необходимо выяснить следующее:

• как быть, если данные поступили из XML-источника, а нам известны только реляционные способы навигации и манипуляции записями;

• и наоборот: как быть, если данные поступили из SQL-совместимой базы данных, а нам известны только XML-совместимые способы навигации и манипуляции записями.

Ответы на эти простые вопросы основаны на применении объекта XmlDataDocument. Он является производным от класса XmlDocument, но обладает расширенными возможностями. Помимо внутренней копии данных, он содержит XML-совместимые средства доступа к ним как к классу XmlDocument с помощью XML-узлов, а также реляционные средства доступа на основе объекта DataSet. Объект XmlDataDocument автоматически синхронизирует оба эти представления (или, иначе говоря, способа доступа) таким образом, чтобы любые изменения, выраженные в одной технологии, сразу же отображались средствами другой технологии. Такой подход позволяет легко смешивать и находить соответствие между разными источниками данных на основе разных технологий.

Для демонстрации этих средств вернитесь к проекту ADO-XML и выполните перечисленные ниже действия.

1. Вставьте еще две кнопки в форму frmXML сразу под кнопкой ExecuteXMLReader, перетаскивая их из панели элементов управления.

2. В окне свойств Properties укажите значение btnNavigateSQL для свойства (Name) и значение Navigate SQL для свойства Text первой кнопки.

3. В окне свойств Properties укажите значение btnAddRows для свойства (Name) и значение Add Rows to XML для свойства Text второй кнопки.

4. Для импорта пространства имен XPath включите строку кода Imports System.Xml.XPath в конце списка команд импорта в верхней части файла с кодом.

5. В код класса frmXML включите две подпрограммы.

Private Sub btnNavigateSQL_Click(ByVal sender As System.Object, _

 ByVal e As System.EventArgs) Handles btnNavigateSQL.Click

 Dim en As New SqlConnection _

  ("data source=localhost;initial catalog=pubs;user id=sa")

 Dim da As New SqlDataAdapter("Select * from authors", cn)

 Dim ds As New DataSet()

 ' Вставка в объект DataSet данных из реляционной базы данных

 da.Fill(ds, "authors")

 ' Создание объекта XmlDataDocument на основе существующего

 ' объекта DataSet.

 Dim xmlDoc As New Xml.XmlDataDocument(ds)

 ' Получение объекта-навигатора из XmlDataDocument.

 Dim xmlNav As XPathNavigator = xmlDoc.CreateNavigator()

 ' Извлечение всех фамилий авторов из штата Калифорния (СА).

 Dim xIterator As XPathNodeIterator xIterator = _

  xmlNav.Select("//authors[state='CA']/au_lname")

 ' Последовательный обход всех выбранных узлов и

 ' отображение фамилий всех авторов.

 Dim str As New System.Text.StringBuilder()

 While (xIterator.MoveNext())

  str.Append(xIterator.Current.Value & ControlChars.CrLf)

 End While

 MsgBox(str.ToString)

End Sub

Private Sub btnAddRows_Click(ByVal sender As System.Object, _

 ByVal e As System.EventArgs) Handles btnAddRows.Click

 Dim dsPubs As New DataSet()

 ' Считывание XML-данных из файла.

 dsPubs.ReadXml("..\Pubs.xml")

 ' Вставка новой записи.

 Dim row As DataRow = dsPubs.Tables("Publishers").NewRow()

 row("pub_name") = "Newbie Publishing Corp."

 row("city") = "New York"

 row("state") = "NY"

 row("Country") = "USA"

 dsPubs.Tables("Publishers").Rows.Add(row)

 ' Связывание объекта DataSet с сеткой Data Grid

 ' для просмотра новых данных.

 grdData.DataMember = "publishers"

 grdData.DataSource = dsPubs

End Sub

Подпрограмма btnNavigateSQL_Click считывает данные из базы данных SQL Server, а затем выполняет обход всех записей, извлеченных с помощью XPATH-запроса. В этой подпрограмме ключевыми являются приведенные ниже строки.

Dim xmlDoc As New Xml.XmlDataDocument(ds)

' Получение объекта-навигатора из XmlDataDocument.

Dim xmlNav As XPathNavigator = xmlDoc.CreateNavigator()

' Извлечение всех фамилий авторов из штата Калифорния (CA).

Dim xIterator As XPathNodeIterator xIterator = _

 xmlNav.Select("//authors[state='CA']/au_lname")

Сначала объект DataSet с данными связывается с новым объектом XmlDataDocument. Затем создается объект-навигатор XPathNavigator на основе объекта-документа XmlDataDocument. После чего на основе объекта XPathNavigator создается объект-итератор XPathNodeIterator. Далее строка XPATH-запроса передается методу Select для возвращения списка фамилий авторов (т.е. значений поля из Калифорнии (т.е. у которых поле state имеет значение СА). После этого выполняется последовательный обход всех узлов, извлеченных данным запросом, и создается строка с искомым перечнем авторов. Эта строка отображается в диалоговом окне, которое показано на рис. 10.3.

РИС. 10.3. Диалоговое окно с XML-данными, извлеченными из объекта DataSet

Вторая подпрограмма, btnAddRows_Clicks, выполняет другую задачу. Сначала она считывает XML-данные с помощью метода ReadXml из файла Pubs. в набор данных dsPubs, как показано в листинге 10.1. Этот метод автоматически создает таблицу publishers в объекте DataSet. Далее новые данные вставляются с помощью реляционных методов и объектов, например объекта-записи DataRow. Новый объект DataRow включается в схему таблицы publishers, а полям присваиваются указанные значения. После этого новая запись вставляется в таблицу publishers, а результат вставки отображается в сетке DataGrid (рис. 10.4).

РИС. 10.4. Вид сетки DataGrid после считывания данных из XML-файла вставки строки

 

Резюме

В этой главе демонстрируется тесная взаимосвязь и интеграция способов доступа к данным и XML на платформе .NET Framework. В частности, показано, что объект DataSet обладает возможностями чтения и записи XML-данных и XML-схем. При отсутствии определения XML-схемы объект DataSet способен вывести его на основании анализа структуры данных в считанном XML-документе.

С другой стороны, объект XmlDataDocument образует мост между реляционным и иерархическим миром XML. Доступ к данным внутри объекта XmlDataDocument можно получить либо XML-совместимым способом, либо реляционным. Изменения в одном представлении автоматически реплицируются в другом представлении. Разработчик базы данных может выбрать наиболее удобный способ доступа к данным и манипулирования ими.

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

 

Вопросы и ответы

Иногда в документации по платформе.NET, помимо XSD-формата схемы данных, упоминается XDR-формат. Что это такое?

XSD-формат — это стандартный формат определения схемы XML-данных, принятый консорциумом World Wide Web Consortium (W3C), который специализируется на создании Web-ориентированных стандартов (более подробную информацию об этом консорциуме можно найти по адресу: www.w3c.org). Еще до окончания работ по созданию стандарта для XSD-формата компания Microsoft решила создать собственный временный формат XML-Data Reduced (XDR) для определений XML-схем. После завершения работы над стандартом XSD-формата в мае 2001 года Microsoft перешла к полной поддержке XSD-формата. Несмотря на то что на платформе .NET Framework схемы данных сохраняются только в XSD-формате, они могут считываться в обоих форматах для совместимости с прежними унаследованными системами. В наборе инструментальных средств разработчика .NET Framework SDK предусмотрена специальная утилита xsd.exe, которая может (помимо прочего) конвертировать схему данных из формата XDR в формат XSD.

Как конкретно ADO.NET автоматически определяет схему данных после анализа XML-документа?

Алгоритм автоматического определения схемы данных хорошо документирован в справочных материалах Visual Studio .NET. Для ознакомления с ним следует обратиться к разделу Inferring DataSet Relational Structure from XML (Автоматическое определение XML-схемы данных после анализа реляционной структуры объекта DataSet). В нем приводится краткое описание алгоритма, некоторые особенности анализа таблиц, полей и отношений, а также ограничения алгоритма. 

В этой главе описаны два способа доступа к данным: реляционный на основе объекта DataSet и иерархический на основе XmlDataDocument. В каких ситуациях следует их применять?

Вообще говоря, не существует единственно верного и универсального способа. Каждый из них следует применять в зависимости от используемых источников данных и поставленной задачи. Платформа .NET Framework позволяет гибко использовать данные из любых источников: реляционных или иерархических. Независимо от источника, данные можно сохранять как реляционные или как иерархические в соответствии с конкретными потребностями. Кроме того, если источник данных и полученные после обработки данные должны иметь формат XML, а вы в недостаточной степени владеете технологиями и инструментами XML, то поставленную задачу все равно можно успешно выполнить с помощью реляционных инструментов на основе объектов DataSet и DataTable.