5. Использование DataSet
5.1. Использование DataSet с существующими данными
5.2. Создание DataSet программным путём
5.3. Добавление DataTable в DataSet
5.4. Добавление отношений между двумя таблицами
5.5. Установка отношений между таблицами
5.6. Добавление ограничений для DataSet
5.6.1. ForeignKeyConstraint
5.6.2.
UniqueConstraint
5.7. Работа с событиями
DataSet
5.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