программирование, создание программ, учебник Delphi, учебник по программированию, бейсек, делфи, си, паскаль
 
 
 

 

Объект DataSet (Набор данных) и сравнение пессимистического блокирования с оптимистическим


Транзакции только помогают сохранить непротиворечивость базы данных. Если вы переводите деньги со сберегательного счета на текущий для оплаты счета за телефон, транзакции помогут гарантировать, что деньги будут сняты с одного счета и появятся на другом (или же не произойдет ни того, ни другого). Вы не столкнетесь ни с ситуацией, когда деньги придут на текущий счет, но не будут сняты со сберегательного (это было бы неплохо для вас, но плохо для банка), ни с противоположной ситуацией (неприятной для вас, но хорошей для банка). Ничто не помешает вашей супруге потратить эти деньги на ужин в модном ресторане.
При оптимистическом блокировании предполагается, что ничего подобного не случится, но вам следует быть готовым к такой ситуации, если она все-таки возникнет. Использование пессимистического блокирования требует координации действий всех пользователей таблицы базы данных таким образом, чтобы предотвратить подобную ситуацию. Разумеется, чем меньше блокировок накладывается на столбец базы данных, тем шире возможности использования вашего приложения.
Следует понимать, что такая ситуация влияет и на считывание данных, и на их обновление. Скажем, если ваша супруга видит, что на счету есть деньги, и строит свои планы относительно этих денег, это может привести к не меньшим проблемам, чем просто потеря денег с общего текущего счета.
Хотя обсуждение способов решения таких проблем выходит далеко за пределы рассматриваемого в этой главе материала, важно помнить, что они возникают, если записи, считанные в объект DataSet (Набор данных), не блокированы. Использование SqlDa-taAdapter при работе с DataSet (Набор данных) предполагает применение оптимистической стратегии блокировки.
Почему это так важно? Прежде всего потому, что от этого зависит производительность и масштабируемость вашего приложения. А почему это так сложно? Потому что нельзя дать совета, подходящего для всех приложений в любой ситуации. Когда пользователи не обращаются одновременно к одним и тем же данным, использование оптимистической стратегии блокировок является наилучшим вариантом. Если необходимо заблокировать доступ к записи на долгий период времени, время ожидания получения доступа к этой записи значительно увеличится, понижая тем самым производительность и масштабируемость приложения.
Вы должны понимать, что такое уровни локализации транзакций, администратор блокировок базы данных, и что существует возможность конфликта при доступе к данным, а такой конфликт может привести к зависанию приложения. Вы должны понимать, сколько времени и ресурсов может потратить ваше приложение на разрешение конфликтов, как оно должно поступать с несогласованными или некорректными данными. Все это необходимо для принятия решения, в каких ситуациях допускается попытка избежать зависания любой ценой, и как следует поступать при возникновении последовательности конфликтующих операций.
Иногда может понадобиться использовать объект DataSet (Набор данных) с дополнительно реализованными возможностями для проверки того, были ли изменены записи, содержащиеся в нем, со времени их последней выборки или модификации. А можно просто использовать SqlDataReader и заново произвести выборку. Все это зависит от ситуации.
Так, при бронировании комнат в нашем примере HotelBroker (Посредник, бронирующий места в гостинице) нельзя делать оптимистических предположений о наличии свободных мест. Это равносильно предположению о бесконечном количестве комнат в отеле и приведет к ситуации, когда администратор должен будет распределить ограниченное количество комнат на гораздо большее количество желающих. В нашем примере для проверки того, зарезервирована ли комната, используется хранимая процедура MakeReservation.
Иногда, даже при отсутствии одновременных запросов, объект DataSet (Набор данных) нельзя использовать для добавления новой строки без установления связи с базой данных. В нашем примере HotelBroker (Посредник, бронирующий места в гостинице) нельзя использовать произвольный первичный ключ. Бронирование могут производить одновременно несколько пользователей. Поэтому идентификаторы бронирования не могут быть локальными. Их определение должно производиться самой базой данных. В нашем примере это делает хранимая процедура MakeReservation.
Способ использования отсоединенных операций в вашем приложении следует определить еще прежде, чем вы решите, каким образом будут использоваться объекты SqlDataReader и DataSet (Набор данных).
Зачем вообще в приложении HotelBroker (Посредник, бронирующий места в гостинице) используется DataSet (Набор данных)? Фактически, в реализации объекта Customer (Клиент) DataSet (Набор данных) никак не используется. Но его использует объект HotelBroker (Посредник, бронирующий места в гостинице), и делается это по двум причинам. Первая — педагогическая. Мы хотели показать, как объект DataSet (Набор данных) может быть использован в полноценном приложении, а не только в простой программе. Во-вторых, в Web-ориентированной версии приложения, реализованной в последующих главах книги, удобно производить кэширование некоторых данных. Например, вполне разумно сделать так, чтобы пользователь мог работать с локальной копией системы бронирования. С другой стороны, такую информацию, как электронный адрес пользователя, достаточно запросить один раз — при его регистрации в системе. Поэтому в нашем случае нет необходимости в сложном механизме кэширования информации о пользователе, так что реализованный объект Customer (Клиент) использует методы объекта SqlCommand.

Множественные таблицы в объекте DataSet (Набор данных)


Каждый объект DataSet (Набор данных) содержит коллекцию из одного или более объектов1,DataTable (Таблица данных). Каждый объект DataTable (Таблица данных) соответствует одной таблице. С помощью свойства SelectCommand, в котором содержится операция соединения, можно производить выборку из нескольких таблиц базы данных в один объект DataTable (Таблица данных). При необходимости обновить содержимое множественных таблиц достаточно определить лишь команду обновления, так как информация о связях между таблицами базы данных уже известна. В файле Hotel-Bookings, h нашего примера свойство SelectCommand объекта SqlDataAdapter, содержащегося в объекте HotelBroker (Посредник, бронирующий места в гостинице), определено следующим образом:
String *cmd = // Строка
"select Customerld, HotelName, City, ArrivalDate, // выбрать
DepartureDate, Reservationld from Reservations, Hotels
where Reservations.Hotelld = Hotels.Hotelld";
// где Резервирование.Hotelld = Гостиницы.Hotelld"; adapter->SelectCommand = new SqlCommand(cmd, conn); dataset = new DataSet; // новый Набор данных adapter->Fill(dataset, "Reservations");
// Заполнить (набор данных, "Резервирование")
В этом случае DataSet (Набор данных) содержит один объект DataTable (Таблица данных), представляющий таблицу, называющуюся Reservations (Резервирование). Информация о том, что некоторые данные получены из таблицы Hotels, не сохраняется.
В один набор данных можно загрузить данные нескольких таблиц. Это продемонстрировано в примере DataSchema, в котором используется база данных Northwind.
adapter->SelectCommand = new SqlCommand(
"select * from [Order Details] where Productld = 1",
// "выбрать * из [Подробности заказа] где Productld = 1 ",
conn);
adapter->FillSchema (
dataset, SchemaType::Source, "Order Details");
// набор данных, SchemaType:: Источник, " Подробности заказа");
adapter->Fill(dataset, "Order Details");
// Заполнить (набор данных, " Подробности заказа");
adapter->SelectCommand =
new SqlCommand("select * from Shippers", conn);
// выбрать * из Грузоотправителей adapter->FillSchema(
dataset, SchemaType::Source, "Shippers");
// набор данных, SchemaType:: Источник, "Грузоотправители"); adapter->Fill(dataset, "Shippers"); // Заполнить (набор данных, "Грузоотправители");
В этом случае объект DataSet (Набор данных) содержит две таблицы, OrderDetails и Shippers. Метод SqlDataAdapter: :FillSchema заполняет DataSet (Набор данных) данными из таблиц, а также информацией о первичных ключах, связанных с таблицами. Затем программа просматривает содержимое таблиц и выводит на печать данные и первичные ключи таблиц. Доступ к содержащимся в DataTable (Таблица данных) объектам DataColumn осуществляется с помощью коллекции Columns (Столбцы), также являющейся частью объекта DataTable (Таблица данных).
lEnumerator *pEnum = dataset->Tables->GetEnumerator();
// набор данных-> Таблицы while (pEnum->MoveNext ()) {
DataTable *t =
dynamic_cast<DataTable *>(pEnum->Current); Console::WriteLine(t->TableName); DataColumn *dc [] = t->PrimaryKey; for (int i=0; i<dc->Length; i++)
{
Console::WriteLine(
"\tPrimary Key Field {0} = {!}", // " \t Поле первичного ключа {0} = {1} ", _box(i),
dc[i]->ColumnName); }
Console::Write("\t"); // Запись
lEnumerator *pEnum = t->Columns->GetEnumerator(); // Столбцы while (pEnum->MoveNext()) {
DataColumn *c =
dynamic_cast<DataColumn *>(pEnum->Current); Console::Write ("{0, -20}", c->ColumnName); // Запись }
Console::WriteLine("");
pEnum = t->Rows->GetEnumerator() ; // Строки while (pEnum->MoveNext()) {
DataRow *r =
dynamic_cast<DataRow *>(pEnum->Current) ; Console::Write("\t"); // Запись for (int i=0; i<r->ItemArray.Length; i++) Console::Write ( // Запись "{0, -20}",
r->get_Item(i)->ToString()->Trim()); // Вырезка Console::WriteLine(""); } }
Программа выводит на экран название таблицы, первичные ключи, названия столбцов и данные таблиц:
Order Details
Primary Key Field 0 = OrderlD
Primary Key Field 1 = ProductID
OrderlD ProductID UnitPrice Quantity Discount
10285 1 14.4 45 0.2
10294 1 14.4 18 0
Shippers
Primary Key Field 0 = ShipperlD ShipperlD CompanyName Phone
1 Speedy Express (503) 555-9831
2 United Package (503) 555-3199
3 Federal Shipping (503) 555-9931
А вот и перевод:
Подробности заказа
Поле первичного ключа 0 = OrderlD
Поле первичного ключа 1 = ProductID
Идентификатор заказа Идентификатор продукта Цена Количество Скидка
10285 1 14.4 45 0.2
10294 1 14.4 18 О
Грузоотправители
Поле первичного ключа 0 = ShipperlD
Идентификатор грузоотправителя Название компании Телефон
1 Быстрый экспресс (503) 555-9831
2 Объединение пакет (503) 555-3199
3 Федеральная отгрузка (503) 555-9931

Создание таблицы без обращения к источнику данных


DataSet (Набор данных) можно использовать как резидентную реляционную базу данных, не связанную ни с какой другой базой данных. Теперь на примере программы DataEditing, мы рассмотрим несколько возможностей объекта DataSet (Набор данных), связанных с добавлением данных и отношений непосредственно в набор данных, без обращения к какой бы то ни было внешней базе данных.
Прежде всего создадим новый объект DataSet (Набор данных) и включим проверку ограничений. Затем добавим в DataSet (Набор данных) четыре объекта DataTable (Таблица данных): Books (Книги), Categories (Категории), Authors (Авторы) и BookCate-gories (Категории книг).
DataSet *ds = new DataSet; // новый Набор данных ds->EnforceConstraints = true; // истина
// Добавить (Add) таблицы (tables) к Набору данных (DataSet) DataTable *categories =
ds->Tables->Add("Categories");
// Таблицы-> Добавить ("Категории"); DataTable *bookcategories =
ds->Tables->Add("BookCategories");
// Таблицы-> Добавить ("BookCategories"); DataTable *authors = ds->Tables->Add("Authors");
// Таблицы-> Добавить ("Авторы"); DataTable *books = ds->Tables->Add("Books"); // Таблицы-> Добавить ("Книги");
Объект DataTable (Таблица данных) содержит коллекцию объектов DataColumn, каждый из которых представляет собой столбец таблицы. Теперь добавим столбцы в определения таблиц.
// определить типы для определений столбцов
Type *stringType = Туре::GetType("System.String");
// Система.Строка
Type *intType = Туре::GetType("System.Int32");
// Определить столбцы для таблиц
// Добавить столбец (column) в таблицу Category (Категория) DataColumn *categoryname =
categories->Columns->Add( // категории-> Столбцы-> Добавить "Category",stringType); // Категория
// Добавить (Add) столбцы (columns) для таблицы BookCategories DataColumn *cn = bookcategories->Columns->Add( // Столбцы-> Добавить
"CategoryName", stringType);
DataColumn *loc = bookcategories->Columns->Add( // Столбцы-> Добавить
"LibraryofCongressNumber", stringType);
// Добавить (Add) столбцы (columns) для таблицы Authors (Авторы) DataColumn *auid = authors->Columns->Add( // авторы-> Столбцы-> Добавить
"AuthorId", intType); authors->Columns->Add( // авторы-> Столбцы-> Добавить
"AuthorLastName", stringType); authors->Columns->Add( // авторы-> Столбцы-> Добавить
"AuthorFirstName", stringType);
// Добавить (Add) столбцы (columns) для таблицы Books (Книги) DataColumn *ISBN = books->Columns->Add( // книги-> Столбцы-> Добавить
"ISBN", stringType);
DataColumn *booksauid = books->Columns->Add( // книги-> Столбцы-> Добавить
"AuthorId", intType);
books->Columns->Add("Title", stringType); // книги-> Столбцы-> Добавить ("Название", stringType); DataColumn *bloc = books->Columns->Add( // книги-> Столбцы-> Добавить
"LibraryofCongressNumber", stringType);

Ограничения и связи


Каждый объект DataTable (Таблица данных) содержит коллекцию объектов Da-taRow. Каждый такой объект представляет строку таблицы. Добавление нового объекта DataRow влияет на ограничения объекта DataColumn (мы предполагаем, что свойство Enf orceConstraints объекта DataSet (Набор данных) имеет значение true (истина)).
Первичные ключи
Существует несколько типов ограничений. Первичный ключ — уникальный ключ для строк таблицы. Другие ограничения единственности определяют единственность каждого значения в столбце (или столбцах). Внешний ключ используется для обозначения того, что значения в столбце являются первичными ключами для другой таблицы объекта DataSet (Набор данных). Первичный ключ объекта DataTable (Таблица данных) является свойством:
// Определить РК для таблицы BookCategories DataColumn *bookcategoriesPK [] =
new DataColumn*[2]; bookcategoriesPK[0] = en; bookcategoriesPK[l] = loc; bookcategories->PrimaryKey = bookcategoriesPK;
// Определить РК для таблицы Authors (Авторы) DataColumn *authorsPK [] =
new DataColumn*[1]; authorsPK[0] = auid; authors->PrimaryKey = authorsPK; // авторы
// Определить РК для таблицы Books (Книги) DataColumn *booksPK [] =
new DataColumn*[1]; booksPK[0] = ISBN; books->PrimaryKey = booksPK; // книги
Ограничения
Для работы со всеми ограничениями помимо первичных ключей используются абстрактный базовый класс Constraint (Ограничение) и его производные классы: UniqueConstraint и ForeignKeyConstraint. Базовый класс обеспечивает возможность помещения ограничения в коллекцию ограничений таблицы. Первичный ключ также регистрируется в этой коллекции как ограничение единственности с именем, генерируемым системой. Для определения, является ли ограничение первичным ключом, используется свойство UniqueConstraint::IsPrimaryKey.
Определим уникальность значений столбца Category таблицы Categories (Категории). Так как последний аргумент метода Add (Добавить) имеет значение false (ложь), это ограничение не будет первичным ключом таблицы. Для этой таблицы мы не определяем первичного ключа, а задаем только ограничения единственности. Вообще говоря, задавать ограничения для значений таблицы не обязательно. Хотя это и нарушает правила реляционной целостности, никто не заставляет вас использовать объект DataSet (Набор данных) реляционным способом. // Определим ограничение единственности // для таблицы Categories (Категории) categones->Constraints->Add ( // категории-> Ограничениям Добавить
"Unique CategoryName Constraint",
// "Уникальное ограничение CategoryName ",
categoryname,
false); // ложь
При использовании внешнего ключа можно определить действия, которые следует выполнить при изменении первичного ключа, с которым он связан. Выбор здесь стандартен: None (Ничего), Cascade (Каскад), SetNull. Для установки значения, принимаемого по умолчанию для этого параметра (он описывается в свойстве Def aultValue объекта DataColumn), используется метод SetDefault. Эти параметры могут быть определены как для условий обновления, так и для условий удаления данных.
В нашем примере внешний ключ определяется таким образом, чтобы все идентификаторы авторов, содержащиеся в таблице Books (Книги), были описаны также и в таблице Authors (Авторы). Другими словами, у каждой книги, зарегистрированной в базе, есть автор, который также зарегистрирован в этой же базе. Мы назвали это ограничение '"Authors->Books". При изменении идентификатора автора правила обновления данных вынуждают объект DataSet (Набор данных) изменить этот идентификатор и во всех остальных строках таблиц на новое значение. Когда идентификатор удаляется, значение для этого идентификатора в строках, описывающих книги, будет установлено пустым.
Если при этом свойство DeleteRule имеет значение Cascade (Каскад), то каскадное удаление будет выполнено для всех таких строк из таблицы Books (Книги). Свойство Ас-ceptRejectRule используется при транзакционном изменении объекта DataSet (Набор данных) и будет рассмотрено ниже. Значение этого свойства определяет, что произойдет при вызове метода AcceptChanges объектов DataSet (Набор данных), Da-taRow или DataTable (Таблица данных). В нашем примере изменения будут произведены последовательно со всеми данными. Другое возможное значение этого свойства — None (не совершать никаких действий).
// Определить FK для таблицы Books (Книги) // (Authorld должен быть в таблице Authors (Авторы)) DataColumn *bookauthorFK [] =
new DataColumn*[1]; bookauthorFK[0] = booksauid; ForeignKeyConstraint *fk =
new ForeignKeyConstraint(
"Authors->Books", authorsPK, bookauthorFK); // Авторы-> Книги
fk->AcceptRe;jectRule = AcceptRejectRule::Cascade; // Каскад fk->DeleteRule = Rule::SetNull; fk->UpdateRule = Rule:rCascade; // Каскад books->Constraints->Add(fk); // книги-> Ограничениям Добавить
Связи между данными
Кроме ограничений для данных, можно задавать связи между ними, для хранения которых используется коллекция DataRelation объекта DataSet (Набор данных). Связи соединяют таблицы таким образом, что вы можете перемещаться от предка к потомку и наоборот. При добавлении связи в коллекцию ограничений автоматически добавляется и соответствующий внешний ключ.
В нашем примере таблица Categories (Категории) сделана предком таблицы Book-Categories (Категории книг) через столбцы Categories (Категории) и CategoryName. Оба столбца, между которыми определяется связь, должны содержать данные одного типа. Эту связь можно использовать для нахождения строк в таблице-потомке или строки в таблице-предке по значению поля, соответствующего связанному столбцу. В нашем примере необходимо также установить связь между столбцами, описывающими номер книги в Библиотеке Конгресса в таблицах Books (Книги) и BookCategory.
// Установим связь между столбцом Categories (Категории) // в таблице BookCategories (Категории книг) и
// столбцом Categories (Категории) в таблице Categories (Категории) ds->Relations->Add( // Отношения-> Добавить
"Category->BookCategories Relation",
// "Категория-> Отношение BookCategories ",
categoryname,
en) ;
// Установим связь между столбцом Library of Congress Number // (Номер книги в Библиотеке Конгресса) таблицы Books (Книги) и // столбцом LOC таблицы BookCategories (Категории книг) ds->Relations->Add( // Отношения-> Добавить
"Book Category LOC->Book LOC Relation",
loc,
bloc);

Получение информации о схеме размещения данных в объекте DataTabie (Таблица данных)
Рассмотрим, как можно получить информацию об объекте DataTabie (Таблица данных), точнее, об ограничениях и ключах этого объекта. В предыдущем примере уже было показано, как получить доступ к объектам Data-Column объекта DataTabie (Таблица данных). Обратите внимание на использование свойства IsPrimaryKey объекта UniqueConstraint для определения, является ли ограничение первичным ключом. Следующий фрагмент взят из примера DataEditing.
pEnum = ds->Tables->GetEnumerator(); // Таблицы
while (pEnum->MoveNext() )
{
DataTabie *t =
dynamic_cast<DataTable *>(pEnum->Current);
Console::WriteLine(" {0}", t->TableName);
Console : :WriteLine ( "\tPrimary Key:")/' // Первичный ключ
for (int i = 0; i < t->PrimaryKey.Length; i++)
{
DataColumn *c = t->PrimaryKey[i]; Console::WriteLine("\t\t{0}", c->ColumnName); }
Console::WriteLine("\tConstraints:" ) ;
lEnumerator *pEnum = t->Constraints->GetEnumerator(); // Ограничения while (pEnum->MoveNext()) {
Constraint *c = // Ограничение
dynamic_cast<Constraint *>(pEnum->Current); // Ограничение
String *constraintName; // Строка // если (с - ForeignKeyConstraint) if (dynamic_cast<ForeignKeyConstraint *>(c) != 0) constraintName =
String::Concat("Foreign Key:", // Строка:: Concat ("Внешний ключ: ", c->ConstraintName);
else if (dynamic_cast<UniqueConstraint *>(c) != 0) {
UniqueConstraint *u = dynamic_cast<UniqueConstraint *>(c); if (u->IsPrimaryKey)
constraintName = "Primary Key"; // Первичный ключ else
constraintName = u->ConstraintName; } else
constraintName = "Unknown Name"; // Неизвестное имя Console::WriteLine("\t\t{0, -40}", constraintName); } }
Напечатанные программой строки приведены ниже. Обратите внимание на то, что определение связей, осуществленное нами с помощью объектов DataRelation, приводит к появлению объектов ForeignKeyConstraint в коллекции ограничений таблицы.
Первичный ключ также появляется в коллекции ограничений как объект UniqueCon-straint. Ограничения, определенные как ограничения единственности или внешние ключи, появляются в коллекции так, как ожидается.
Categories
Primary Key: Constraints:
Unique CategoryName Constraint BookCategories Primary Key:
CategoryName
LibraryofCongressNumber Constraints:
Primary Key
Foreign Key:Category->BookCategories Relation
Constraint2 Authors
Primary Key:
Authorld Constraints:
Primary Key Books
Primary Key:
ISBN Constraints:
Primary Key
Foreign Key:Authors->Books
Foreign Key:Book Category LOC->Book LOG Relation
Вот перевод этой выдачи:
Категории
Первичный ключ: Ограничения:
Ограничение единственности CategoryName Категории книг
Первичный ключ:
Название категории
Номер в Библиотеке Конгресса Ограничения:
Первичный ключ
Внешний ключ: Отношение Category-> BookCategories
Constraint2 Авторы
Первичный ключ:
Authorld Ограничения:
Первичный ключ Книги
Первичный ключ:
ISBN Ограничения:
Первичный ключ:
Внешний ключ:Авторы-> Книги
Внешний ключ: Отношение Book Category LOC->Book LOG
Обратите внимание на ограничение в таблице BookCategories (Категории книг) с именем, сгенерированным системой. Когда вы внимательно просмотрите исходный код программы, то убедитесь, что эти ограничения в ней не добавляются. Откуда же они берутся? Если бы вы просмотрели содержимое соответствующего объекта, то увидели бы, что ограничение наложено на столбец LibraryofCongressNumber. Система посчитала, что, поскольку столбец CategoryName является внешним ключом для другой таблицы, то значения в столбце LibraryOfCongressNumber должны быть уникальными.
Можно также просмотреть коллекцию связей Relations (Отношения) объекта DataSet (Набор данных). При этом можно узнать, какие таблицы являются предками, и какие именно столбцы в них участвуют в образовании связей. То же самое можно сделать и для таблиц-потомков. Приведем соответствующий фрагмент примера DataEditing.
pEnum = ds->Relations->GetEnumerator(); // Отношения
while (pEnum->MoveNext())
{
DataRelation *dr =
dynamic_cast<DataRelation *>(pEnum->Current); DataTable ^parentTable = dr->ParentTable; DataTable *childTable = dr->ChildTable; Console::WriteLine(
" Relation: {0} ", dr->RelationName); // Отношение Console::WriteLine(
ParentTable: {0, -10}", parentTable); Console::Write(" Columns: "); // Столбцы forfint j =0; j < dr->ParentColumns.Length; j++) Console::Write( // Запись
{0, -10}",
dr->ParentColumns[j]->ColumnName); Console::WriteLine(); Console::WriteLine(
ChildTable: (0, -10}", childTable);
Console::Write(" Columns: "); // Запись forfint j = 0; j < dr->ChildColumns.Length; j++) Console::Write( // Запись
{0, -10}",
dr->ChildColumns[j]->ColumnName); Console::WriteLine(); }
Программа напечатает:
Output Relations between tables in the DataSet... Relation: Category->BookCategones Relation ParentTable: Categories
Columns: Category ChildTable: BookCategories
Columns: CategoryName Relation: Book Category LOC->Book LOG Relation ParentTable: BookCategories
Columns: LibraryofCongressNumber
ChildTable: Books
Columns: LibraryofCongressNumber
А вот и перевод:
Отношения между таблицами в Наборе данных...
Отношение: Категория-> Отношение Категории книг ParentTable: Категории
Столбцы: Категория ChildTable: Категории книг
Столбцы: CategoryName Отношение: Отношение Категория книги LOC->Book LOC ParentTable: Категории книг
Столбцы: Номер в Библиотеке Конгресса ChildTable: Книги
Столбцы: Номер в Библиотеке Конгресса

Изменение объекта DataRow


При необходимости внести значительные изменения в объект DataSet (Набор данных) и отложить при этом проверку ограничений и событий, можно использовать режим редактирования набора данных.
BeginEdit. EndEdit, CancelEdit
Переход в режим редактирования осуществляется вызовом метода BeginEdit объекта строки, которую необходимо изменить. Для выхода из этого режима используются методы EndEdit и CancelEdit.
В примере DataEditing мы нарушаем ограничение, заданное внешним ключом, добавляя в таблицу Books (Книги) строку, описывающую книгу автора, идентификатор которого отсутствует в базе данных. Исключение, являющееся результатом этого нарушения, возникнет только после вызова метода EndEdit. Так, при выполнении следующего фрагмента файла DataEditing. h примера DataEditing исключение не возникает.
DataRow *rowToEdit = books->Rows->get_Item(0); // книги-> Строки
rowToEdit->BeginEdit();
try
{
rowToEdit->set_Item("Author!d", _box(21));
}
catch(Exception *e) // Исключение
{
Console::WriteLine(
"\n {0] while editing a row.", e->Message); // при редактировании строки, Сообщение); Console::WriteLine(); }
Однако исключение возникает, как только в программе вызывается метод EndEdit.
try {
rowToEdit->EndEdit(); }
catch(Exception *e) // Исключение {
Console::WriteLine();
Console::WriteLine(
"\n{0} on EndEdit", e->Message); // Сообщение
Console::WriteLine(); }
В результате будет напечатано следующее сообщение, указывающее на то, что по окончании сеанса изменения содержимого строки обнаружено нарушение ограничения.
ForeignKeyConstraint Authors->Books requires the child key values (21) to exist in the parent table, on EndEdit
Версии объекта DataRow
До того, как будут подтверждены внесенные в строку изменения, доступны и исходные, и измененные значения полей строки. С помощью свойства элемента строки28 Da-taRowVersion можно определить, какое именно значение вы хотите использовать. Это свойство может иметь значения Original (Первоначальное), Default (Заданное по умолчанию), Current (Текущее) и Proposed (Предложенное).
В следующем фрагменте примера DataEditing приведен код, выполняющийся до вызова метода EndEdit:
DataRow *rowToEdit = books->Rows->get_Item(0);
// книги-> Строки rowToEdit->BeginEdit() ; try {
rowToEdit->set_Item("AuthorId", _box(21)); Console::WriteLine(
"Book Author Id Field Current Value {0}",
// Текущее значение поля идентификатора автора книги
rowToEdit->get_Item(
"Authorld", DataRowVersion::Current)); Console::WriteLine(
"Book Author Id Field Proposed Value {0}",
// Предложенное значение поля идентификатора автора книги
rowToEdit->get_Item(
"Authorld", DataRowVersion::Proposed)); // Предложенное Console::WriteLine(
"Book Author Id Field Default Value {0}",
// Значение по умолчанию поля идентификатора автора книги
rowToEdit->get_Item(
"Authorld", DataRowVersion::Default));
// Значение по умолчанию }
В результате программа напечатает:
Book Author Id Field Current Value I Book Author Id Field Proposed Value 21 Book Author Id Field Default Value 21
Вот перевод:
Текущее значение поля идентификатора автора книги 1
Предложенное значение поля идентификатора автора книги 21
Значение по умолчанию поля идентификатора автора книги 21
При выполнении транзакционного редактирования доступны значения Current (Текущее) и Proposed (Предложенное). После вызова метода CancelEdit значение Proposed (Предложенное) становится недоступным. После вызова метода EndEdit значение, имевшее атрибут Proposed (Предложенное) меняет атрибут на Current (Текущее) а значение, имевшее атрибут Proposed (Предложенное), становится недоступным.
Свойство RowState объекта DataRow
Кроме того, что в режиме редактирования доступны значения поля Current (Текущее) и Proposed (Предложенное), сам объект DataRow имеет свойство, описывающее состояние соответствующей строки. Это свойство может принимать значения Added (Добавлено), Deleted (Удалено), Detached (Отсоединено), Modified (Изменено) или Unchanged (He изменено).
Строка находится в состоянии Detached (Отсоединено) в случаях, когда она создана, но либо еще не добавлена ни в одну коллекцию объектов DataRow, либо удалена из какой-нибудь коллекции.
Какое из значений будет возвращено при использовании значения Default (Заданное по умолчанию) свойства DataRowVersion определяется значением свойства RowState.
Принятие или отмена изменений
Вызов метода EndEdit объекта DataRow не приводит к фиксации изменений, сделанных в строке. Вызов методов AcceptChanges или RejectChanges объектов DataSet (Набор данных), DataTable (Таблица данных) или DataRow приводит к выходу из режима транзакционного редактирования для всех строк соответствующего объекта. Если до этого не были вызваны EndEdit или CancelEdit, AcceptChanges или Rejec-tChanges вызывают эти методы для всех строк соответствующего объекта.
После вызова метода AcceptChanges значения, имевшие атрибут Proposed (Предложенное) становятся основными (т.е. имеющими атрибут Original (Первоначальное)). Если при этом свойство RowState имело значение Added (Добавлено), Modified (Изменено) или Deleted (Удалено), ему присваивается значение Unchanged (He изменено), а сами изменения вступают в силу (т.е. строки добавляются, изменяются или удаляются).
После выполнения метода RejectChanges значение, имевшее атрибут Proposed (Предложенное), удаляется. Если при этом свойство RowState имело значение Deleted (Удалено) или Modified (Изменено), значение поля становится прежним и свойству RowState присваивается значение Unchanged (He изменено). Если же RowState имело значение Added (Добавлено), строка удаляется из коллекции Rows (Строки).
Так как после вызова метода AcceptChanges свойство RowState имеет значение Unchanged (He изменено), вызов метода Update (Обновить) объекта DataAdapter не приведет к каким-либо изменениям базы данных. Поэтому, при необходимости внести изменения в базу данных, метод Update (Обновить) следует вызывать до вызова метода AcceptChanges строки, таблицы или объекта DataSet (Набор данных).
Ниже приведен фрагмент реализации метода CancelReservation класса HotelBroker (Посредник, бронирующий места в гостинице) из примера CaseStudy. Фрагмент взят из файла HotelBookings . h, находящегося в папке CaseStudy\HotelBrokerAdmin\Hotel. Обратите внимание, что метод AcceptChanges объекта DataSet (Набор данных) вызывается при успешном завершении работы метода SqlDataAdapter: : Update (Обновить). В случае же возникновения исключения вызывается метод RejectChanges.
void CancelReservation(int id) // идентификатор
{
DataTable *t = 0;
try
{
t = dataset->Tables->get_Item("Reservations");
// набор данных-> Таблицы-> get_Item ("Резервирование");
DataRow *rc [] = t->Select ( // Выбор
String::Format("Reservationld = {0} ", // Строка:: Формат
id.ToString())); // идентификатор for (int i=0; i<rc->Length; i++) re[i]->Delete(); // Удалить
int NumberRows = adapter->Update( // Обновление dataset, "Reservations"); // набор данных, "Резервирование"); if (NumberRows > 0) // если (NumberRows> 0)
t->AcceptChanges () ; else
t->RejectChanges () ; }
catch(Exception *e) // Исключение {
t->RejectChanges();
throw e; }
return;
}
Если вы не будете отменять внесенные изменения в случае возникновения ошибки, измененные строки останутся в объекте DataSet (Набор данных). Тогда, при попытке произвести следующее обновление, оно также будет отменено из-за того, что строки все еще не обновлены и наличие их приводит к возникновению исключения. Поскольку объект DataSet (Набор данных) независим от других баз данных, тот факт, что данные в базе данных были обновлены, не имеет никакого отношения к принятию или отмене произведенных изменений данных в самом объекте DataSet (Набор данных).
Ошибки объекта DataRow
Если при изменении данных строки произошла ошибка, свойство HasError объекта DataSet (Набор данных), DataTable (Таблица данных) или DataRow примет значение true (истина). Для получения информации об ошибке используются методы GetCol-umnError или GetCoiunmsInError.

Пример приложения Acme Travel Agency (Туристическое агентство Acme)


К этому моменту мы изложили более чем достаточно материала, необходимого для понимания классов Customer (Клиент) и HotelBroker (Посредник, бронирующий места в гостинице) из версии приложения Acme Travel Agency (Туристическое агентство Acme), ориентированной на работу с базами данных. Как обычно, файлы с исходным кодом для этой версии находятся в папке CaseStudy. Если вы использовали программы, изменяющие содержимое базы данных HotelBroker (Посредник, бронирующий места в гостинице), не забудьте запустить макрос SQL, приводящий эту базу в исходное состояние.
В связи с тем, что у нас не было необходимости хранить какое-либо состояние объекта Customer (Клиент), в нем для доступа к базе данных и получения данных используется объект SqlDataReader Любое состояние, которое может понадобиться программе (например, список клиентов), легко может быть получено у программы-клиента, а не у объекта среднего яруса. Объекты HotelBroker (Посредник, бронирующий места в гостинице) и HotelBookings немного более сложны. Как уже было сказано, из педагогических побуждении эти объекты были реализованы с использованием объекта Data-Set (Набор данных). Так сделано для того, чтобы продемонстрировать использование этой технологии в приложениях. Тем не менее, мы увидим, что при разработке Web-ориентированых приложении есть причины сохранять некоторые состояния в среднем ярусе. В этом случае объект DataSet (Набор данных) служит интеллектуальным кэшем.
А теперь отвлечемся от примера и рассмотрим интеграцию ХМL с базой данных.

Доступ к данным XML


Как будет показано в главе "Web-службы", XML имеет много преимуществ при описании данных, которые нужно перемещать между разнородными системами и источниками данных. Поскольку вы можете обеспечить данные XML описанием схемы данных XML, во многих случаях имеет смысл передавать именно такие данные, а не DataSet (Набор данных). Так как данные ХМL являются текстом, они могут проходить через порты брандмауэров, которые обычно открыты, в отличие от протокола распределенной модели компонентных объектов DCOM (Distributed COM — Distributed Component Object Model) или протокола RMI, используемого в JAVA, которые требуют открытия особых портов.
Мы не ставим себе цель обсудить в следующих разделах все детали языка XML. Мы хотим только продемонстрировать, как можно использовать концепции данных, принятые в ХМL и в DataSet (Набор данных).

Схема и данные XML


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


XmlDataDocument


Документы могут содержать в себе результат вычислений, полученный от базы данных. Например, отчет о продажах содержит, кроме данных о продажах, полученных от источника данных, и некоторые пояснения. Для представления данных в виде документа ХМL используется класс XmlDataDocument.
Класс XmlDataDocument является производным от класса XmlDocument, который представляет документы XML в библиотеке классов .Net Xml Framework. Особенно удобным делает класс XmlDataDocument то, что экземпляр этого класса можно получить из объекта DataSet (Набор данных) посредством простой передачи объекта Data-Set (Набор данных) конструктору класса XmlDataDocument в качестве аргумента. XmlDataDocument имеет свойство DataSet (Набор данных), так что вы можете работать с документом XML как с реляционными данными, если это имеет смысл.

DataSet (Набор данных) и XML


Объект DataSet (Набор данных) содержит методы WriteXml и WriteXmlSchema, которые выдают данные и схему данных, хранящихся в наборе данных. Схема XML, возвращаемая объектом DataSet (Набор данных), определяется из самих данных. Пока вы не добавите явным образом в объект DataSet (Набор данных) ограничения, такие, как первичные или внешние ключи, они не будут частью схемы.
Объект DataSet (Набор данных) содержит также и методы, предназначенные для чтения XML: ReadXml и ReadXmlSchema. С помощью ReadXml можно считывать данные и схему в объект DataSet (Набор данных). Когда схема отсутствует, метод попытается извлечь ее изданных. Если же это не удастся, возникнет исключение. ReadXmlSchema считывает схему документа.
При отсутствии в документе XML схемы, DataSet (Набор данных) будет извлекать данные, как если бы они были таблицами, руководствуясь при этом набором правил. Оставшиеся элементы будут считаться столбцами таблиц
Для определения того, будет столбец записываться в документ XML как элемент или как атрибут, используется свойство Col-rr.Kapping объекта DataColamn. Запись столбца как элемента предпочтительней. Элемент, содержащий нескалярные данные, считается таблицей. Атрибуты и скалярные значения являются столбцами. Подробнее эти правила описаны в документации к .NET.

База данных AirlineBrokers


Для рассмотрения доступа к данным XML мы будем использовать базу данных Air-linesBrokers. Указанную базу данных можно создать и инициализировать с помощью SqlServer Query Manager и SQL-скрипта, прилагаемого к примерам для этой главы. База данных AirlinesBrokers имеет функциональные возможности, используемые системой бронирования Acme. С ее помощью клиенты Acme могут бронировать авиабилеты. Такая база данных содержит следующие таблицы:
• Airlines (Авиалинии): информация об авиалиниях;
• PlaneType (Тип самолета): типы самолетов, используемые на авиалиниях;
• Flights (Рейсы): информация о рейсах на различных авиалиниях:
• Customers (Клиенты): информация о клиентах;
• Reservations (Резервирование): информация о забронированных клиентами местах.
Хотя в реальной жизни списки клиентов систем AirlinesBroker и HotelBroker (Посредник, бронирующий места в гостинице) вряд ли могут совпасть, в нашем примере мы для простоты будем использовать те же таблицы Customers (Клиенты) и те же компоненты для доступа к данным, ч го и ранее.

DataSet (Набор данных) и XML


Для иллюстрации связи между реляционной моделью объекта DataSet (Набор данных) и моделью ХМ L прежде всего извлечем некоторую информацию из базы данных. В примере DataSetXml для этого используются те же команды и способы, что и ранее в этой главе.
Первым делом создадим соединение, набор данных и преобразователь данных для различных таблиц.
SqlConnect]on *conn =
new SqlConnect K>n ( connectStr ing) ;
DatdSet *d - new Pdtr.Set ( "Ад r±ineBroker") ; // новый Набор данных SqlDjtaAd.-pter "air ] i -n^s Adaptcr =
new Sq]DataAoar te L ; SqiDctaAaapter * f 1 ^qn4- sAdapter -
new SqlLataAdapter; SqlDataAdapter ^plar.etypeAdapter =
new SqlDdtdAdapter; SqlDataAdapter *custon,ersAdaptei =
new SqlDataAaapter; SqlDataAdapter ^reservationsAaapter -
new SqlDataAaapter;
Затем создадим несколько команд select (выбрать) для получения данных, и, используя эти команды, заполним набор данных данными из таблиц.
airlinesAdapter->SelectCommand =
new SqlCommand("select * from Airlines", conn);
// "выбрать * из Авиалиний " airlinesAdapter->Fill(d, "Airlines"); // Заполнить (d, "Авиалинии");
flightsAdapter->SelectCommand =
new SqlCommand("select * from Flights", conn);
// "выбрать * из Рейсов" flightsAdapter->Fill(d, "Flights"); // Заполнить (d, "Рейсы");
planetypeAdapter->SelectCommand =
new SqlCommand("select * from PlaneType", conn); // выбрать planetypeAdapter->Fill(d, "PlaneType"); // Заполнить
customersAdapter->SelectCommand =
new SqlCommand("select * from Customers", conn);
// "выбрать * из Клиентов " customersAdapter->Fill(d, "Customers"); // Заполнить (d, "Клиенты");
reservationsAdapter->SelectCommand =
new SqlCommand("select * from Reservations", conn);
// "выбрать * из Резервирования " reservationsAdapter->Fill(d, "Reservations"); // Заполнить (d, "Резервирование");
Теперь в объекте DataSet (Набор данных) есть данные таблиц Airlines (Авиалинии), PlaneType (Тип Самолета), Flights (Рейсы), Customers (Клиенты) и Reservations (Резервирование). Далее извлечем из данных объекта DataSet (Набор данных) схему XML. Затем извлечем сами данные и запишем их в формате XML.
d->WriteXmlSchema("Airlines.xsd");
d->WriteXml("Airlines.xml");
Приведенные операторы создают два файла: Airlines . xsd и Airlines . xml. Ниже вы видите некоторые данные, записанные в Airlines. xml. Главным элементом является AirlineBroker; именно так назывался объект DataSet (Набор данных). На один уровень ниже находятся элементы, соответствующие разным таблицам объекта DataSet (Набор данных): Airlines (Авиалинии), PlaneType (Тип самолета), Flights (Рейсы) и Customers (Клиенты). О забронированных местах информации в базе данных не было. В получившемся документе каждой строке исходных таблиц соответствует одна запись. Элементы этих записей соответствуют полям исходных таблиц.
<?xml version="l.О" standalone="yes"?> <AirlineBroker> <Airlines>
<Name>America West</Name>
<Abbreviation>AW</Abbreviation>
<WebSite>www.americawest.com</WebSite>
<ReservationNumber>555-555-1212</ReservationNumber> </Airlines> <Airlines>
<Name>Delta</Name>
<Abbreviation>DL</Abbreviation>
<WebSite>www.delta.com</WebSite>
<ReservationNumber>800-456-7890</ReservationNumber>
</Airlines>
<Flights>
<Airline>DIX/Airlj.n£>
<FlightNurrber>98^</FlightNumber>
<StartCity>Atlanta</StartCity>
<EndCity>New Orleans</EndCity>
<Departure>2001-10-05T20:15:00.0000000-04:00 </Departure>
<Amval>2001-10-05T22 : 30: 00. 0000000-04 : 00</Amval>
<PlaneType>737</?ianeType>
<FirstCost>1300</FirstCost>
<BusinessCost>0</BusinessCost>
<EconoiryCost>450</EconomyCost> </Flights>
<PlaneType>
<PlaneType> 737 </PlaneType>
<FirstClass>10</FirstClass>
<BusinessClass>0</BusinessClass>
<EconomyClass>200</EconomyClass> </PlaneType->
<Customers>
<LastName>Adams</LastName> <FirstName>John</FirstName>
<EmailAddress>adams@presidents.org</EmailAddress> <CustomerId>1</CustomerId> </Custorrers> </AirlineBroker>
Вот более русифицированная версия этого XML-документа:
<? xml версия = "1.0" автономный = "да"?> <AirlineBroker> <Авиалинии>
<Название> Американский Запад </Название>
<Сокращение> AW </Сокращение>
<УзелИеЬ> www.americawest.com </УзелМеЬ>
<ReservationNumber> 555-5Ь5-1212 </ReservationNumber> </Авиалинии> <Авиалинии>
<Название> Дельта </Название>
<Сокращение> DL </Сокращение>
<Узeлweb> www.delta.com </УзeлWeb>
<ReservationNumber> 800-456-7890 </ReservationNumber> </Авиалинии> <Рейсы>
<Авиалиния> DL </Авиалиния> <FlightNumber> 987 </FlightNumber> <StartCity> Атланта </StartCity> <EndCity> Новый Орлеан </EndCity> <Вьшет> 2001-10-05Т20:15:00.0000000-04:00
</Вылет> <Прибытие> 2001-10-05Т22:30:00.0000000-04:00 </Прибытие>
<Тип самолета> 737 </Тип самолета> <F.L-(Cust> 1300 </FirstCost> <Bus _ncssCost> 0 </BusinessCost> <EconcmyCost> 450 </EconomyCost> </Рейсь:>
<Тип самолета>
<Тип самолета>737</Тип самолета>
<FirstClass> 10 </FirstClass>
<BusinessClass> 0 </BusinessClass>
<EconomyClass> 200 </EconomyClass> </Тип самолета>
<Клиенты>
<LastName> Адаме </LastName> <FirstName> Джон </FirstName>
<EmailAddress> adams@presidents.org </EmailAddress> <CustomerId> 1 </CustomerId> </Клиенты> </AirlineBroker>
Исходя из этих данных, объект DataSet (Набор данных) создал схему и сохранил ее в файле Airlines.xsd. Дальше мы обсудим некоторые отрывки из этого файла. В нем нет информации о связях или первичных ключах какой бы то ни было таблицы, такой, как Airlines (Авиалинии) или Flights (Рейсы), по той простой причине, что они не были определены в исходной базе данных. Если вы просмотрите созданный файл, вы увидите, что в нем записана и информация о схеме данных таблицы Reservations (Резервирование), несмотря на то, что в этой таблице нет никаких данных.
В первой строке заголовка схемы определено название схемы (AirlineBroker). Кроме того, в нем определены два пространства имен, используемых в этой схеме документа. Одно пространство имен, названное xsd, содержит описание стандарта схемы XML. Второе, названное msdata, содержит описание от Microsoft.
<xsd:schema id="AirlineBroker" targetNamespace="" xmlns="" xmlns:xsd=http://www.w3.org/2001/XMLSchema xmlns:rnsdata="urn:schemas-microsoft-com:xml-msdata">
В следующей строке описывается элемент под названием AirlineBroker, имеющий атрибут, указывающий, что эта схема получена из объекта DataSet (Набор данных). Это атрибут в определениях Microsoft, а не в пространстве имен W3C Schema. Элемент AirlineBroker относится к составному (не скалярному) типу, т.е. является структурой, состоящей из элементов других типов. Такая структура может содержать произвольное количество элементов (или не содержать ни одного) любого типа, определенного в остальной части схемы.
<xsd:element name="AirlineBroker" msdata:IsDataSet="true"> <xsd:complexType> <xsd:choice maxOccurs="unbounded">
Далее описывается элемент, определяющий тип данных. Этот тип — тоже структура и потому относится к составному (не скалярному) типу, очередность элементов в котором совпадает с очередностью их определения в объекте DataSet (Набор данных). Так уж получилось, что все элементы, соответствующие столбцам таблицы базы данных, определены здесь как имеющие строковый тип string, причем их напичие не считается обязательным. В исходной таблице первичные ключи не определялись, а так как все эти строки в записях базы данных обязательны, объект DataSet (Набор данных) при преобразовании данных вывел и\ из набора таблиц, ограничений и связей, определенных в объекте DataSet (Набор данных) в момент преобразования.
<xsd : element r.ame = "Airlines"> <xsd:conplexType> <xsd:sequence>
<xsd: element narre = "Kame" type="xsd:string"
minOccurs="0" /> <xsd: element narr:e="Abbreviation"
type="xsd:string" minOccurs="0" /> <xsd:element name="KebSite" rype="xsd:string"
minOccurs="0" /> <xsd:element name="ReservationNumber"
type="xsd:string" minOccurs="0" /> </xsd:sequence> </xsd:complexType> </xsd:element>
Таблица Flights (Рейсы) определена аналогичным образом. Кроме того, что в ней не определен первичный ключ, в ней нет и внешних ключей для столбцов Airline (Авиалиния) и Plane Type (Тип самолета).
<xsd: element na.me = "Fliqhts"> <xsd:complexType> <xsd:sequence>
<xsd:element name="Airline" type="xsd:string"
minOccurs="0" /> <xsd:element name="FlightNumber" type="xsd:int"
rainOccurs="0" /> <xsd:element name="StartCity" type="xsd:string"
minOccurs="0" /> <xsd:element name="EndCity" type="xsd:string"
minOccurs="C" /> <xsd:element name="Derarture" type="xsd:dateTime"
minOccurs="0" />
<xsd:element name="Arrival" type="xsd:dateTime"
minOccurs="0" />
<xsd:element name="PlaneType" type="xsd:string"
minOccurs="0" />
<xsd:element name="FirstCost" type="xsd:decimal"
minOccurs="0" /> <xsd:element name="BusinessCost"
type="xsd:decimal" minOccurs="0" /> <xsd:element name="EconomyCost"
type="xsa:decimal" minOccurs="0" /> </xsd:sequence> </xsd: ccm,plexType> </xsd:element>
</xsd:choice>
</xsd:complexType> </xsd:element> </xsd:schema>
К этому определению схемы данных мы еще вернемся позже, а сейчас продолжим рассмотрение примера.

Создание документа XML из объекта DataSet (Набор данных)


Используя объект DataSet (Набор данных) можно создать новый документ XML. Используя запрос XPath, можно перейти в начало документа, а затем, с помощью объекта XmlNodeReader прочитать весь документ. Мы выведем содержимое документа на экран. Класс XmlNodeReader обеспечивает перемещение по документу. Приведем фрагмент кода из примера DataSetXML:
XmlDataDocument *xmlDataDoc = new XmlDataDocument(d);
XmlNodeReader *xmlNodeReader = 0;
try
{
XmlNode *node = xmlDataDoc->SelectSingleNode("/");
XmlNodeReader = new XmlNodeReader (node);
FormatXml (XmlNodeReader); }
catch (Exception *e) // Исключение {
Console::WriteLine (
"Exception: {0}", e->ToString()); // Исключение
}
finally // наконец
r
if (XmlNodeReader != 0) // если (XmlNodeReader! = 0)
xmlNodeReader->Close(); }
static void FormatXml (XmlReader *reader) {
while (reader->Read()) // читатель-> Чтение () {
switch (reader->NodeType) // переключатель
//(читатель-> NodeType) {
case XmlNodeType::Element: // случай
// XmlNodeType::Элемент Format (reader, "Element"); // Формат (читатель, "Элемент"); while(reader->MoveToNextAttribute() ) Format (reader, "Attribute"); // Формат (читатель, "Атрибут"); break;
case XmlNodeType::Text: // случай XmlNodeType:: Текст Format (reader, "Text"); // Формат (читатель, "Текст"); break;
static String *lastNodeType = ""; // статическая Строка
static void Format(XmlReader *reader, String *nodeType) // Формат
{
if (nodeType->Equals("Element"))
// если (nodeType-> Равняется ("Элемент"))
{
if (lastNodeType->Equals("Element"))
// если (lastNodeType-> Равняется ("Элемент"))
{
Console::WriteLine();
}
for (int i=0; i < reader->Depth; i++)
{
Console::Write(" "); // Запись
}
Console::Write(reader->Name) ;
// Запись:: (читатель-> Название); }
else if (nodeType->Equals("Text")) // если (nodeType-> Равняется ("Текст")) Console::WriteLine("={0}", reader->Value); // Значение else
{
Console::Write(String::Format( // Запись:: (Строка:: Формат ( "{0}<{1}>{2}", nodeType, reader->Name, // Название reader->Value)); // читатель-> Значение Console::WriteLine (); }
lastNodeType = nodeType; }
Вот какой документ XML будет записан объектом DataSet (Набор данных) в файл:
AirlineBroker
Airlines <!— Авиалинии -->
Name=America West
Abbreviation=AW
WebSite=www.americawest.com
ReservationNumber=555-555-1212
Airlines <!-- Авиалинии —>
Name=Delta
Abbreviation=DL
WebSite=www.delta.com
ReservationNumber=800-456-7890
Airlines <!-- Авиалинии -->
Name=Northwest
Abbreviation=NW
WebSite=www.northwest.com
ReservationNumber=888-111-2222
Airlines <!— Авиалинии —>
Name=Piedmont
Abbreviation=P
WebSite=www.piedmont.com
ReservationNumber=888-222-333
Airlines <!-- Авиалинии -->
Name=Southwest
Abbreviation's
WebSite=www.southwest.com
ReservationNumber=l-800-111-222
Airlines <!-- Авиалк-^'и -->
Name=Unitea
Abbreviation=UAL
WebSite=www.ual.com
ReservationNumber=800-123-4568
Flights <'-- Рейсъ. -->
Airline=DL
FlightNumber=987
StartCity=Atlanta
EndCity=New Orleans
Departure=2001-10-05T2G:15:СС.ООСГПСО-04:00
Arnval=2001-10-05T22:30:ОС.ОЭООССО-04:СО
PlaneType=737
FirstCost=1300
BusinessCost=0
EconomyCost=450
Flights <!-- Рейсы -->
Airline=UAL
FlightNumber=54
StartCity=Boston
EndCity=Los Angeles
Departure=2001-10-01T10:00:OO.OOCOOOO-r4:00
Arriva1=2001-10-01T13:00:00.0000000-04:00
PlaneType=767
FirstCost=1500
BusinessCost=1000
EconomyCost=300
PlaneType
PlaneType=737
FirstClass=10
BusinessCldss=0
EconomyСlass=200
PlaneType
PlaneType=767
FirstClass=10
BusinessClass=30
EconomyCiass=300
Customers !'-- 1лкеггы -->
LastName=Adams
FirstName=John
EmailAddress=adans@presidents.erg
Customerld

Резюме


ADO.NET содержит классы, позвопяющие создавать и использовать распределенные данные Вы можете работать с базами данных в соединенном или отсоединенном режимах, в зависимости от потребностей Объект Dt,L_Set (Набор данных) дает возможность работать с данными реляционным способом даже при отсутствии соединения с каким-либо источником данных Для модетирования реляционных данных можно использовать документы XML, содержащие информацию в нереляционном виде Типизированный DataSet (Набор данных) облегчает работу, обеспечивая определение схемы XML для данных

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика