5. Использование DataSet5.1.
Использование DataSet с существующими данными5.2.
Создание DataSet программным путём5.3.
Добавление DataTable в DataSet5.4.
Добавление отношений между двумя таблицами5.5.
Установка отношений между таблицами5.6.
Добавление ограничений для DataSet 5.6.1.
ForeignKeyConstraint 5.6.2.
UniqueConstraint5.7.
Работа с событиями DataSet5.8.
Использование Typed DataSet
5. Использование DataSet
Чтобы появилась возможность использования DataSet, Вы
должны включить System.Data:
using
System.Data
Есть два основные способа использования объекта DataSet: в
рамках существующей системы управления базой данных (database
management system) или создание нового DataSet программным
путём.
- Используйте существующую систему управления базы
данных, например SQL Server, для заполнения таблицы в DataSet.
В этом методе, используйте одну SQLDataSetCommand для таблицы,
чтобы заполнить объект DataTable данными. Однако, Вы должны
создать объекты DataRelation между всеми таблицами.
- Вы
можете создавать DataSet программным путём, добавлять объекты
DataTable к нему и затем создавать объекты DataRelation, чтобы
организовать связи для каждой таблицы. Используйте программный
путь, когда Вам необходимо создать DataSet в процессе
выполнения программы, чтобы увидеть на экране данные,
сгенерированные приложением.
В типовом решении, данные запрашиваются приложением клиента
посредством URL. Когда запрос достигает соответствующего
компонента промежуточного слоя, DataSet будет создан с помощью
специального адаптера. После этого, DataSet конвертируется в
XML документ для транспортировки назад к инициатору
запроса.
В клиентском приложении данные отображаются с
использованием комбинации средств управления, например,
DataGrid. Пользователь может добавлять, удалять, или
редактировать данные. После окончания работы с данными
пользователем, DataSet снова конвертируется в документ XML для
передачи назад к компоненту сервера. Компонент промежуточного
слоя конвертирует документ XML назад в DataSet, и использует
адаптер, чтобы попытаться применить изменения данные в системе
управления базой данных. Если будут обнаружены коллизии
данных, компонент сервера может урегулировать их, используя
встроенные бизнес-правила. Урегулированный DataSet будет
возвращён назад клиенту. После этого, DataSet может быть
применён в существующий DataSet, и пользователь может
продолжить с ним работу, причём, DataSet после этого будет
отражать самую последнюю версию.
Потенциальная потеря точности для десятичного типа
данных (Decimal)
DataSet хранит данные, используя типы,
имеющиеся у COM+. Для большинства прикладных программ, они
обеспечивают удобное представление информации источника
данных. Однако, это представление может причинять проблему,
когда тип в источнике данных - SQL decimal. Тип COM + decimal
позволяет иметь максимум 28 знаков цифр, в то время как тип
SQL decimal позволяет иметь 38 знаков цифр. В настоящее время,
FillDataSet метод вызывает исключение, если он сталкивается с
правильным типом SQL decimal, но большим, чем 28 знаковых
цифр. Пока нет никакого способа получить SQL decimal больших
чем 28 знаковых цифр в объекте DataSet. Если ваше приложение
требует большей точности, используйте объект SQLDataReader и
запрос метода GetSQLNumeric, что бы получить необходимое
значение SQL decimal.
5.1. Использование DataSet с существующими
данными
Рассмотрим общепринятые способы использования DataSet с
существующими данными:
1. Создайте и заполните каждую
таблицу в DataSet данными системы управления базой данных,
используя SQLDataSetCommand или ADODataSetCommand. См.
Создание простой DataSetCommand.
2. Измените данные в
индивидуальных объектах DataTable, добавляя, модифицируя или
удаляя объекты DataRow.
3. Вызовите метод GetChanges для
создания второго DataSet, который будет показывать только
изменения в данных. Метод требует один параметр, DataRowState
для изменений, в которых Вы заинтересованы. Приведённый ниже
пример использует метод возвращающий DataSet, содержащий
только изменённые строки:
[VB]
Dim changedDataSet As
DataSet
changedDataSet =
ds.GetChanges(DataRowState.Modified)
[C#]
System.Data.DataSet
changedDataSet;
changedDataSet =
ds.GetChanges(DataRowState.Modified);
4. Проверьте ошибки во втором DataSet, исследуя его
свойство HasErrors, посредством которого можно узнать, имеет
ли ошибки какая-либо из таблиц в наборе.
5. Если ошибки
присутствуют, проверьте свойство HasErrors каждой DataTable.
Если таблица имеет ошибки, вызовите метод GetErrors класса
DataTable, который возвратит массив объектов DataRow с
ошибками.
6. Для каждого DataRow, исследуйте свойство
RowError для того, что бы определить к каким данным это
относится.
7. Урегулируйте ошибки, если это возможно.
8. Вызовите метод Merge, что бы объединить изменения во
втором DataSet с первым.
[VB]
ds.Merge(changedDataSet)
[C#]
ds.Merge(changedDataSet);
9. Вызовите метод Update для SQLDataSetCommand (или
ADODataSetCommand), используя объединенный DataSet как
параметр.
[VB]
workDSCMD.Update (ds)
[C#]
workDSCMD.Update (ds);
10. Вызовите метод AcceptChanges для DataSet, что бы
принять изменения, или альтернативный метод RejectChanges,
чтобы отменить изменения.
ds.AcceptChanges
5.2. Создание DataSet программным путём
DataSet обеспечивается двумя основными конструкциями:
public
DataSet()
public DataSet(string DataSetName)
Первая конструкция для пустого DataSet, вторая создаёт
DataSet с указанным именем.
Следующий пример создает
экземпляр DataSet:
[VB]
Dim workDataSet as DataSet
Set
workDataSet As New DataSet() ' Empty DataSet.
' Or
Set
workDataSet As New DataSet("CustomerOrders")
[C#]
DataSet workDataSet = new
DataSet(); // Empty DataSet.
// Or
DataSet workDataSet =
new DataSet("CustomerOrders");
5.3. Добавление DataTable в DataSet
ADO.NET дает Вам возможность вначале создать внешние
таблицы, а затем добавлять их к существующему набору данных
DataSet. (чтобы создать DataTable, см. .Net Framework SDK
Создание DataTable)
Обратите внимание, для текущей
версии, Вы должны добавить недавно созданную таблицу к DataSet
до загрузки её данными.
Следующий пример создает DataSet
(ds), добавляет новый DataTable с именем Orders и добавляет
три столбца (OrderID, OrderQuantity, и CustID) к объекту
DataTable. Наконец, этот код определяет столбец OrderID как
первичный ключ.
[VB]
ds.Tables.Add(New
DataTable("Orders"))
ds.Tables("Orders").Columns.Add("OrderID",
System.Type.GetType("System.Int32"))
ds.Tables("Orders").Columns.Add("OrderQuantity",
_
System.Type.GetType("System.Int32"))
ds.Tables("Orders").Columns.Add("CustID",
_
System.Type.GetType("System.Int32"))
ds.Tables("Orders").PrimaryKey
= New DataColumn(ds.Tables.Columns("Orders"), _
ds.Tables.Columns("OrderID"))
' Or
Dim
workTable As DataTable = New
DataTable("Orders")
workTable.Columns.Add("OrderID",
System.Type.GetType("System.Int32"))
workTable.Columns.Add("OrderQuantity",
System.Type.GetType("System.Int32"))
workTable.Columns.Add("CustID",
System.Type.GetType("System.Int32"))
ds.Tables("Orders").PrimaryKey
= _
New
DataColumn(ds.Tables("Orders").Columns("OrderID"))
ds.Tables.Add(workTable)
[C#]
ds.Tables.Add(new
DataTable("Orders"));
ds.Tables["Orders"].Columns.Add("OrderID",
System.Type.GetType("System.Int32"));
ds.Tables["Orders"].Columns.Add("OrderQuantity",
System.Type.GetType("System.Int32"));
ds.Tables["Orders"].Columns.Add("CustID",
System.Type.GetType("System.Int32"));
ds.Tables["Orders"].PrimaryKey
=
new
DataColumn[]{ds.Tables["Orders"].Columns["OrderID"]};
//
Or
DataTable workTable = new
DataTable("Orders");
workTable.Columns.Add("OrderID",
System.Type.GetType("System.Int32"));
workTable.Columns.Add("OrderQuantity",
System.Type.GetType("System.Int32"));
workTable.Columns.Add("CustID",
System.Type.GetType("System.Int32"));
ds.Tables["Orders"].PrimaryKey
=
new
DataColumn[]{ds.Tables["Orders"].Columns["OrderID"]};
ds.Tables.Add(workTable);
5.4. Добавление отношений между двумя
таблицами
Поскольку DataSet может содержать множество объектов
DataTable, также должен быть способ связать все таблицы друг с
другом. Это может быть необходимо для организации навигации по
таблицам, и для возвращения связанных
значений.
Необходимыми параметрами DataRelation являются
два столбца, которые служат как первичные ключи и foreign
ключи в отношениях таблиц, и имя DataRelation. Имя может
использоваться для навигации, или при поиске
значений.
Примечание: Отношения также могут быть построены
не только по одному столбцу в таблице. Вы можете использовать
массив объектов DataColumn для первичных и foreign
ключей.
Представленный ниже пример предполагает, что в
DataSet существуют два объекта DataTable. Обе таблицы имеют
столбец по имени CustID, который служит для связи между
таблицами. Пример добавляет один DataRelation к совокупности
отношений объекта DataSet. Первый параметр (CustOrders)
определяет имя отношений. Второй и третий параметры - объекты
DataColumn, которые связывают две таблицы.
[VB]
Dim ds As DataSet = New
DataSet("CustomerOrders")
' With ds do the
following:
' -
add a customer table with CustID and CustName as
columns.
' -
set Customers.CustID as the PrimaryKey column.
' - add an Orders table
with three columns: OrderID, CustID, and Quantity
' - set Orders.OrderID
as the PrimaryKey column.
' Add a DataRelation to the
Relations collection specifying its name, and the
'
appropriate DataColumn objects as
arguments.
ds.Relations.Add("CustOrders",
ds.Tables("Customers").Columns("CustID"), _
ds.Tables("Orders").Columns("CustID"))
'
Or
Dim dr As DataRelation = _
New
DataRelation("CustOrders",
ds.Tables("Customers").Columns("CustID"), _
ds.Tables("Orders").Columns("CustID"))
ds.Relations.Add(dr)
[C#]
DataSet ds = new
DataSet("CustomerOrders");
// With ds do
the following:
// - add a customer table with CustID and CustName as
columns
// - set Customers.CustID as Primary Key
// - add an
Orders table with three columns: OrderID, CustID, and
Quantity
// - set Orders.OrderID as primary key
// Add a DataRelation
to the Relations collection specifying its name,
// and the appropriate
DataColumn objects as
arguments.
ds.Relations.Add("CustOrders",ds.Tables["Customers"].Columns["CustID"],
ds.Tables["Orders"].Columns["CustID"]);
//
Or
DataRelation dr;
dr = new
DataRelation("CustOrders",ds.Tables["Customers"].Columns["CustID"],
ds.Tables["Orders"].Columns["CustID"]);
ds.Relations.Add(dr)
5.5. Установка отношений между таблицами
В начале, функция DataRelation должна обеспечить навигацию
от одной таблицы к другой в пределах DataSet. Это позволит
привязать все связанные объекты DataRow одной таблицы, по
заданной, единственной DataRow из связанной с ней таблицы.
Например, если дана DataRow таблицы Customers, Вы можете
получить все orders относящиеся к клиенту из таблицы
Orders.
Представленный ниже пример кода возвращает
множество объектов DataRow (orders) из одной таблицы,
используя DataRelation и единственную DataRow из другой
(customers).
[VB]
Dim x() As DataRow =
ds.Tables("Customers").ChildRelations("CustOrders").
_
GetChildRows(ds.Tables("Customers").Rows(0))
Console.WriteLine("")
Console.WriteLine("Total
Child records for CustOrders Relationship = " & _
x.Count.ToString)
Console.WriteLine("Total Tables in
DataSet = " & ds.Tables.Count.ToString)
[C#]
DataRow[] x =
ds.Tables["Customers"].ChildRelations["CustOrders"]
.GetChildRows(ds.Tables["Customers"].Rows[0]);
Console.WriteLine("");
Console.WriteLine("Total
Child records for CustOrders Relationship = "
+
x.Count.ToString());
Console.WriteLine("Total
Tables in DataSet = " + ds.Tables.Count.ToString());
5.6. Добавление ограничений для DataSet
В реляционной базе данных должна обеспечиваться целостность
данных. Один из путей поддержания такой целостности в ADO.NET,
это добавление ограничений к таблицам. Ограничение, это
автоматически исполняемое правило, которое определяет порядок
действий, когда содержание строки так или иначе изменено. Есть
два вида ограничений, доступных в ADO.NET:
ForeignKeyConstraint и UniqueConstraint.
5.6.1. ForeignKeyConstraint
Когда значение в строке удалено или обновлено, и это
значение также используется в одной или более связанных
таблиц, ForeignKeyConstraint определяет то, что случается во
второй таблице, которая связана с той же самой колонкой через
DataRelation. Возможные действия включают:
Действие |
Описание |
Cascade |
Удаление или обновление связанных строк.
|
SetNull |
Установка значений в связанных строках к
null. |
SetDefault |
Установка значений в связанных строках к
значениям по умолчанию. |
None |
No action — значения в связанных строках
не затронуты. |
Default |
Заданное по умолчанию действие для
свойства, которое должно применятся при каскадном
действии. |
Используя ForeignKeyConstraint сначала создают ограничение,
а затем устанавливают свойства DeleteRule и UpdateRule к
одному из соответствующих значений, как показано в примере
ниже:
[VB]
Dim fk As ForeignKeyConstraint =
_
New
ForeignKeyConstraint(ds.Tables("Customers").Columns("CustID"),
_
ds.Tables("Orders").Columns("CustID"))
fk.DeleteRule =
Cascade
fk.UpdateRule = SetDefault
' Add the constraint
to the Customers
table.
ds.Tables(0).Constraints("Customers").Constraints.Add(fk)
[C#]
ForeignKeyConstraint fk = new
ForeignKeyConstraint(ds.Tables
["Customers"].Columns["CustID"],
ds.Tables["Orders"].Columns["CustID"]);
fk.DeleteRule =
Cascade;
fk.UpdateRule =
SetDefault;
ds.Tables["Customers"].Constraints.Add(fk);
5.6.2. UniqueConstraint
Когда обязательно, чтобы все значения в столбце были
уникальны, Вы можете добавить к DataTable UniqueConstraint.
UniqueConstraint может быть назначен или для отдельного
столбца, или на массив столбцов того же самого объекта
DataTable.
[VB]
Dim uc As UniqueConstraint =
_
New
UniqueConstraint(ds.Tables("Customers").Columns.("CustID"))
'
Add the constraint to the Customers
table.
ds.Tables("Customers").Constraints.Add(uc)
[C#]
Data.UniqueConstraint uc =
new
UniqueConstraint(ds.Tables("Customers").Columns.("CustID");
//
Add the constraint to the Customers
table.
ds.Tables["Customers"].Constraints.Add(uc);
5.7. Работа с событиями DataSet
DataSet обеспечивает ряд событий, которые могут быть
использованы в коде пользователя. Этот события включают:
-
PropertyChange
- MergeFailed
- RemoveTable
-
RemoveRelation
- Добавление обработчика события (event
handler) к событию.
[VB]
ds.AddOnPropertyChange(new
System.ComponentModel.PropertyChangeEventHandler
_
(AddressOf
me.DataPropertyChange))
ds.AddOnMergeFailed(new
System.Data.MergeFailedEventHandler _
(AddressOf
me.DataSetMergeFailed))
Private Sub
DataSetPropertyChange _
(ByVal sender As
Object, ByVal e As System.PropertyChangeEventArgs)
MessageBox.Show("DataSet property change")
End
Sub
Private Sub DataSetMegeFailed _
(ByVal sender As
Object, ByVal e As System.Data.MergeFaileedEventArgs)
MessageBox.Show("Merge failed!")
End Sub
[C#]
ds.AddOnPropertyChange
(new
System.ComponentModel.PropertyChangeEventHandler(DataSetPropertyChange));
ds.AddOnMergeFailed
(new
System.Data.MergeFailedEventHandler(DataSetMergeFailed));
private
void DataSetPropertyChange
(object sender,
System.PropertyChangeEventArgs e){
MessageBox.Show("DataSet property
change")}
private void DataSetMergeFailed
(object sender,
System.Data.MergeFailedEventArgs e){
MessageBox.Show("Merge failed!")}
5.8. Использование Typed DataSet
ADO обеспечивает отложенно-связанный доступ (late bound
access) к значениям в пределах его рекордсета через слабо
типизированные переменные. ADO.NET обеспечивает способность
обратиться к данным, содержащимся в DataSet через метафору
"Strongly-Typed". Это означает, что Вы можете обращаться к
таблицам и столбцам, которые являются частью DataSet через
простые имена и strongly typed переменные.
Typed DataSet -
класс, который происходит от DataSet. Также, он наследует все
методы и свойства DataSet. Однако, в дополнение, typed DataSet
обеспечивает методы "strongly-typed". На практике это
означает, что Вы можете обращаться к таблицам и столбцам по
имени, вместо использования collection based
метода.
Например, в ADO Вы могли использовать следующий код
Visual Basic, чтобы найти определенную запись в рекордсете, и
изменить значение поля:
AdoRecordSet.Find("Customerid =
'ALFKI'")
AdoRecordSet.Fields("FirstName").Value =
"Bob"
Для typed DataSet в ADO.NET, тот же самый результат был бы
получен с помощью следующего кода:
CustomerDataSet.Customers("ALFKI").CustomerName =
"Bob"
Кроме удобочитаемости кода, typed DataSet позволяет
транслятору автоматически завершать строки, которые Вы
пишете.
Кроме того, strongly-typed DataSet обеспечивает
доступ к значениям строки как правильным strongly typed
значениям. В ADO Вы работали через варианты при назначении и
поиске данных. Если бы значение, которое Вы назначили, имело
бы неправильный тип, ADO выдал бы ошибку во время выполнения
программы. В ADO.NET, если значение - целое число, и Вы
пытаетесь назначать строку, Вы получите статическую
ошибку.
Учитывая схему XML, которая поддерживает XSD
стандарт, Вы можете генерировать typed DataSet используя
программу XSD.EXE, поставляемую с .NET Framework SDK.
Синтаксис генерации DataSet следующий:
xsd.exe /d /l:C#
{XSDSchemaFileName.xsd}
Где:
/D – директива, сообщающая программе о
необходимости генерировать DataSet;
/l: - сообщает
программе какой язык использовать. Для обозначения языка Вы
можете использовать C# или VB.
Представленный ниже пример использует typed DataSet с
именем customerDataSet, чтобы загрузить список customers
(заказчиков) из базы данных Northwind. Как только данные
загружены, используя метод FillDataSet, организуется цикл по
каждому заказчику в таблице customers, используя объект typed
customerRow. Здесь, Вы имеете прямой доступ к полю customerid,
в противоположность доступу к этому полю через совокупность
полей (fields collection).
[C#]
customerDataSet myCustDS = new
customerDataSet();
SQLDataSetCommand myDSCommand = new
SQLDataSetCommand("SELECT * FROM
Customers",
"server=localhost;uid=sa;pwd=;database=northwind");
myDSCommand.FillDataSet(myCustDS,
"Customers");
foreach(customersRow myCustomer in
myCustDS.customers)
{
Console.WriteLine(myCustomer.CustomerID.ToString());
}
[VB]
Dim myCustDS as DataSet = new
dataset
Dim myDSCommand as SQLDataSetCommand
myDSCommand
= new SQLDataSetCommand("Select * from
customers",
"server=localhost;uid=sa;pwd=;database=northwind")
myDSCommand.FillDataSet(myCustDS,
"Customers")
Dim myCustomer as DataRow
For Each
myCustomer in
myCustDS.Tables(0).Rows
Console.WriteLine(myCustomer(0).ToString())
Next