Оставляя в стороне маркетинговую шумиху, следует отметить, что 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.
В главах 5, "ADO.NET: объект DataSet", и 6, "ADO.NET: объект DataAdapter" демонстрируются программируемые и прямые способы загрузки данных в объект DataSet из базы данных. Еще один метод загрузки данных основан на чтении XML-данных. Как и следовало ожидать, в объект DataSet данные можно записывать в XML-формате. Более того, объект DataSet обладает средствами чтения и записи 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.
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
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-данных.
После загрузки в объект DataSet (независимо от способа и места загрузки) данные и/или схемы данных можно записать в XML-формате (с XML-схемой или без нее). Для демонстрации способов записи данных в XML-формате выполните перечисленные ниже действия.
1. Создайте в форме новую кнопку сразу под кнопкой Read XML, перетаскивая ее из панели инструментов.
2. В окне свойств Properties укажите значение btnWriteXML для свойства (Name) и значение Write XML для свойства Text этой кнопки.
3. Затем в определении класса формы frmXML введите код из листинга 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")
End Sub
В этой подпрограмме создаются два объекта — адаптера данных (daAuthors и daPublishers), которые затем используются для вставки данных в объект dsPubs из двух таблиц базы данных pubs СУБД SQL Server. В листинге 10.4 приведено содержимое файла StoreSales.xml, который создается в результате выполнения этой подпрограммы. Обратите внимание на то, что данный XML-документ содержит записи о продажах, а затем записи о магазинах. Этот подход имеет смысл, так как между ними не задано никакого отношения. Если бы таблицы Sales и Stores были связаны, то эти записи можно было вложить друг в друга. Пример такого вложения приводится далее, в бизнес-ситуации 10.1.
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оrd_num>
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, полученного в результате выполнения этого метода.
Рассмотрим теперь формат 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" (вставлена).
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езультате выполнения этого метода.
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
Компания 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.
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.
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.
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.
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
В главе 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.
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("" & reader.Name & ">" & 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
В главе 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.