Операції над даними з ієрархічною структурою. Розробка розподілених додатків в. NET




Зміст



Введення


Ця стаття демонструє методику читання і запису ієрархічних наборів рядків
у джерелі даних. У прикладах коду, наведених у цій статті, для з'єднання
з базою даних Microsoft® SQL Server ™ або
Microsoft® Desktop Engine (MSDE) використовується
керований провайдер SQL (SQL managed provider). Для з'єднання з іншими
OLEDB-сумісними джерелами даних слід застосовувати керований
провайдер ADO (ADO managed provider).

Для доступу до ієрархічним рядках, що повертається джерелом даних,
в ADO.NET використовуються об'єкти DataReader іDataSet. Об'єкт
DataReader забезпечує простий і швидкий доступ до даних тільки для читання.
За допомогою цього об'єкта можна звертатися або до ієрархічним рядків даних,
отриманими в результаті виконання декількох операторів SELECT, або
доXML-даними, повертаним SQL Server 2000. Об'єкт DataReader
дозволяє читати дані тільки в напрямку вперед (forward-only)
і залишається з'єднаним з базою даних, поки додаток читає дані.

Об'єкт DataSet, Навпаки, являє від'єднаний реляційний кеш
даних у пам'яті. Він може містити кілька таблиць, що розглядаються як
окремі або пов'язані таблиці. Особливості цього об'єкта полегшують навігацію
по зв'язаних даних і їх модифікацію.

Крім того, SQL Server. NET Data Provider дозволяє отримати
XML-потік безпосередньо від SQL Server 2000. Для цього надається
спеціальна API-функція, ExecuteXmlReader, Доступна через
об'єкт SQLCommand. Метод ExecuteXmlReader виконує
SQL-запит стосовноSQL-з'єднанню і на основі
XML, повернутого запитом, створює об'єкт XmlReader.
ExecuteXmlReader використовується тільки у виразах, результатом яких
є XML-дані, і ефективний у запитах, де у виразах
з SELECT є блок FOR XML.

Операції читання


Ієрархічні рядки часто структуровані як кілька наборів пов'язаних
рядків. Для більшої ефективності часто краще витягувати кілька наборів
рядків за одне звернення до бази даних, ніж окремо запитувати кожен
з них. Зазвичай для цього виконується пакет SQL-виразів (batch
of SQL expressions) або збережена процедура з кількома виразами
SELECT. Крім того, якщо ви використовуєте в операторі SELECT блок
FOR XML, SQL Server 2000 повертає ієрархічні рядки у вигляді XML.

Застосування ADO.NET-об'єкта DataReader


У наступному прикладі виконується пакет з двох SQL-виразів
SELECT і відповідні набори результатів (result sets) витягуються
через об'єкт DataReader. Цей об'єкт забезпечує навігацію за даними
у напрямку тільки вперед, так що для подальшої роботи дані швидше за все
слід перенести в інший контейнер. Так як DataReader залишає
з'єднання з базою даної відкритим, з точки зору масштабованості дані треба
переносити швидко і як можна швидше закривати з'єднання. Передавати об'єкт
DataReader між рівнями розподіленого додатки не можна.

Для перебору рядка в наборах результатів застосовуються методи
NextResult іRead об'єкта DataReader.

Dim sqlCmd As SQLCommand
Dim sqlDataRdr As SQLDataReader
Dim fld As Integer
Dim rptLine As String
Try
"Підготувати об'єкт команди, виконуючий збережену процедуру,
"Яка повертає Orders і OrderDetails
sqlCmd = New SQLCommand()
With sqlCmd
.CommandType = CommandType.StoredProcedure
.CommandText = "GetOrders"
.Connection = New SqlConnection(myConnString)
End With
"Відкрити з'єднання
sqlCmd.Connection.Open()
"Виконати команду; результат передається в DataReader
sqlDataRdr = sqlCmd.ExecuteReader()
"Перебирати рядки в першому наборі результатів
Do
While (sqlDataRdr.Read)
"Тут з рядком можна виконати будь-які дії
rptLine = ""
For fld = 0 To sqlDataRdr.FieldCount – 1
rptLine = rptLine & sqlDataRdr.Item (fld). ToString
If fld < sqlDataRdr.FieldCount – 1 Then
rptLine = rptLine & ", "
End If
Next
End While
"Перемістити вказівник на наступний набір результатів
If Not (sqlDataRdr.NextResult) Then
Exit Do
End If
Loop
Catch E As Exception
Finally
"Закрити DataReader
sqlDataRdr.Close()
End Try


Застосування XmlReader і ADO.NET-об'єкта SqlCommand


У Microsoft SQL Server 2000 вбудована підтримка XML. Щоб результати
виразів SELECT поверталися у вигляді XML, у цих виразах потрібно
вказувати блок FOR XML. Метод ExecuteXmlReader об'єкта
SQLCommand дозволяє отримувати XML-дані безпосередньо від SQL
Server 2000. ExecuteXmlReader повертає об'єкт
System.Xml.XmlReader, Що містить XML-дані, отримані
від SQL Server 2000.

У наступному прикладі збережена процедура, яка використовує FOR XML,
запускається викликом методу ExecuteXmlReader об'єкта SQLCommand.

Dim sqlConn As SqlConnection
Dim sqlCmd As SqlCommand
Dim xmlRdr As XmlReader
Try
"Створити новий об'єкт з'єднання
sqlConn = New SqlConnection(myConnString)
"Створити новий об'єкт команди
sqlCmd = New SqlCommand()
"Вказати виконувану команду
With sqlCmd
.CommandType = CommandType.StoredProcedure
.CommandText = "GetOrdersXML"
.Connection = sqlConn
End With
"Відкрити з'єднання
sqlConn.Open()
"Виконати команду і витягти рядок у DataReader
xmlRdr = sqlCmd.ExecuteXmlReader()
"Перейти до кореневого елементу
xmlRdr.MoveToContent()
"Щось робимо з даними
outXML = xmlRdr.ReadOuterXml
"Перейти до наступного елементу
xmlRdr.MoveToElement()
"Вважати атрибут OrderId поточного елемента
xmlRdr.MoveToAttribute("OrderId")

Catch e As Exception
"Опрацювати виняток
Finally
sqlConn.Close()
End Try

 

Застосування ADO.NET-об'єкта DataSet


Об'єкт DataAdapter витягає дані з джерела і заповнює об'єкти
DataTable всередині DataSet. Для виконання запитів до бази даних
об'єкту DataAdapter потрібно об'єкт Connection.

Якщо запит повертає кілька наборів результатів, DataSet
зберігає кожен з них у окремій таблиці. Між таблицями може існувати
відношення (relationship).

Відношення між таблицями


Як тільки ви пов'язуєте дві таблиці вDataSet через ставлення
(За допомогою об'єкта DataRelation), Навігація по них спрощується. Крім
того, зв'язування полегшує вибірку всіх дочірніх рядків (об'єктів DataRow)
однієї таблиці для батьківської рядка в іншій таблиці (об'єкті
DataTable). Щоб відібрати дочірніх рядків використовується перевантажений метод
GetChildRows об'єкта DataRow.

Відносини встановлюються створенням об'єкта DataRelation, Який
зіставляє рядки однієї таблиці з рядками інший. Ці відносини зберігаються
в об'єкті DataRelationCollection, Який міститься в об'єкті
DataSet.

Пов'язувати таблиці в об'єкті DataSet не обов'язково. Їх можна залишити
непов'язаними. Однак, якщо між двома таблицями існують
будь-які відносини, наприклад через зовнішній ключ (foreign key
relation), то зв'язування таблиць спростить доступ до дочірніх рядках в одному
об'єкті DataTable з батьківської рядка в іншому об'єкті
DataTable.

У наступному прикладі коду демонструється вибірка замовлень та їх позицій (order
details) з таблиць Orders і OrderDetails. Об'єкт DataSet містить таблиці
Orders і Details, відповідні таблиць в базі даних. Зв'язком між двома
таблицями служить стовбець 'OrderId', присутній в обох об'єктах
DataTables.

Dim sqlDA As SqlDataAdapter
Dim hierDS As DataSet
Dim orderRow As DataRow
Dim detailRow As DataRow
Dim detailRows() As DataRow
Dim i As Integer
Try
"Створити новий об'єкт DataAdapter
sqlDA = New SqlDataAdapter()
"Створити новий об'єкт DataSet
hierDS = New DataSet()
"Поставити зіставлення таблиць
sqlDA.TableMappings.Add ("Orders", "Orders")
sqlDA.TableMappings.Add ("Orders1", "OrderDetails")
With sqlDA
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду об'єкта SelectCommand
With .SelectCommand
.CommandType = CommandType.StoredProcedure
.CommandText = "GetOrders"
.Connection = New SqlConnection(myConnString)
End With
"Заповнити DataSet повертаються даними
.Fill(hierDS, "Orders")
End With

Так як до виклику методу Fill в об'єкті DataSet немає таблиць,
об'єкт SQLDataAdapter автоматично створює таблиці для DataSet
і заповнює їх повертаються даними. Якщо таблиці створені до виклику
Fill, Об'єкт SQLDataAdapter просто заповнює існуючі таблиці.

 "Вказати первинний ключ для таблиць
hierDS.Tables ("Orders"). PrimaryKey = New DataColumn () _
{HierDS.Tables ("Orders"). Columns ("OrderId")}
hierDS.Tables ("OrderDetails"). PrimaryKey = New DataColumn () _
{HierDS.Tables ("OrderDetails"). Columns ("OrderDetailId")}
"Встановити між двома таблицями ставлення через зовнішній ключ
hierDS.Relations.Add("Order_Detail", _
hierDS.Tables ("Orders"). Columns ("OrderId"), _
hierDS.Tables ("OrderDetails"). Columns ("OrderId"))
"Вибрати одне замовлення з таблиці
orderRow = hierDS.Tables("Orders").Rows(0)
"Вибрати відповідні йому дочірні рядки
detailRows = orderRow.GetChildRows("Order_Detail")
"Робота з колекцією дочірніх рядків
For i = 0 To detailRows.Length – 1
detailRow = detailRows(i)
"Щось робимо з рядком таблиці OrderDetails
strDetail = detailRow ("OrderId"). ToString & "," & _
detailRow ("OrderDetailId"). ToString & "," & _
detailRow ("ItemId"). ToString & "," & _
detailRow ("UnitPrice"). ToString & "," &
detailRow("Quantity").ToString)
Next
Catch E As Exception
"Опрацювати виняток
Finally
"Закрити з'єднання і виконати іншу очищення
End Try


Операції запису


Фіксація (committing) змін в ієрархічних даних, що містять кілька
наборів результатів з двох або більше пов'язаних таблиць, вимагає збереження
цілісності даних. Наприклад, посилальна цілісність означає, що зовнішній ключ
в будь-якій посилається таблиці (referencing table) повинен вказувати на існуючу
рядок у таблиці, на яку робиться посилання (referenced table). Отже,
батьківську рядок у цій таблиці не можна видаляти до тих пір, поки на неї є
посилання в іншій таблиці. Точно так само в посилається таблицю не можна вставляти
рядки, якщо немає відповідних рядків у таблиці, на яку вона посилається.

Так як ADO.NET-об'єкт DataSet дозволяє витягати,
обробляти і модифікувати дані в базі, він гарантує посилальну
цілісність таблиць при додаванні, зміну та видаленні рядків. Крім того, цей
об'єкт дозволяє виконувати каскадні оновлення та видалення з збереженням
цілісності даних.

Застосування ADO.NET-об'єкта DataAdapter


Метод Update об'єкта DataAdapter передає зміни,
кешовані в об'єкті DataSet, Джерела даних. Для додавання нових
рядків DataAdapter використовує InsertCommand, Для зміни рядків –
UpdateCommand, А для видалення рядків з бази даних –
DeleteCommand. Коли ви викликаєте метод Update, DataAdapter
аналізує змінені рядки і визначає, який з об'єктів Command
потрібно виконати для передачі відкладених змін в кожному рядку.

Перш ніж викликати Update, Ви повинні налаштувати властивості
InsertCommand, UpdateCommand або DeleteCommand —
в залежності від того, які зміни були внесені в дані вDataSet.
Наприклад, якщо зDataSet віддалялися рядки, слід встановити властивість
DeleteCommand. Для автоматичного формування команд Insert,
Update іDelete можна задіяти переваги об'єкту
CommandBuilder. Якщо ви вказуєте
DataAdapter-Властивості InsertCommand,
UpdateCommand або DeleteCommand, Метод Update
відповідно виконує команди insert, update або delete для кожної
вставленої, оновленої чи віддаленій рядка вDataSet. В іншому випадку
CommandBuilder– Залежно від значення властивості SelectCommand
об'єкта DataAdapter– Генерує SQL-команди, необхідні
для внесення змін у базу даних. Тому, щоб CommandBuilder
генерував команди Insert, Update іDelete, Ви повинні
відповідно налаштувати властивість SelectCommand.

Краще всього самостійно вказувати InsertCommand,
DeleteCommand або UpdateCommand, Оскільки це дозволяє вам явно
контролювати, як виконується оновлення, і підвищує продуктивність
в порівнянні з тістом випадком, коли команди генеруються автоматично. Це
особливо важливо, якщо ваш код за одне звернення до джерела даних виконує
операції над кількома рядками, щоб скоротити частоту обміну даними
з сервером.

Для властивостей InsertCommand, UpdateCommand іDeleteCommand
об'єкта DataAdapter можна вказувати параметризрвані запити або
збережені процедури. Параметри в параметризованих запитах або процедурах
відповідають стовпцям в об'єкті DataTable. Таким чином, один об'єкт
DataAdapter підтримує оновлення тільки однієї таблиці у вашій базі
даних. Тому при оновленні бази даних для кожної таблиці в об'єкті
DataSet буде потрібно окремий об'єкт DataAdapter.

Додавання рядків


У дочірню таблицю рядки можна додавати, тільки якщо вони відповідають
допустимим (вже існуючих) рядкам батьківського таблиці. Рядки,
не відповідають допустимим рядках батьківського таблиці, додавати не можна.

Нову рядок потрібно вставити спочатку в батьківську таблицю; тільки після
цього в дочірню таблицю можна додавати відповідні рядки. З тієї ж
причини в процесі синхронізації змін з базою даних метод Update
об'єкта DataAdapter, Відповідного батьківського таблиці, слід
викликати першим.

Автоматичне формування команди Insert


Проблема з автоматично генеруються командами вставки в тому, що об'єкту
DataSet не повертається первинний ключ Id для стовпця Identity. Для
вирішення цієї проблеми ми використовуємо збережену процедуру, яка повертає
первинний ключ для батьківської рядка. Тоді з'являється можливість застосовувати
автоматично формуються команди вставки для дочірніх рядків. У наступному
прикладі ми створюємо два об'єкти DataAdapter, Що заповнюють дві таблиці
в одному об'єкті DataSet. Ми задаємо відношення між цими двома таблицями
і вставляємо в них нові рядки. Метод Update об'єкта DataAdapter,
відповідного батьківського таблиці (у нашому випадку – Order),
викликається першим. Потім викликається метод Update об'єкта
DataAdapter, Відповідного дочірньої таблиці (у нашому випадку –
OrderDetails).

Dim sqlConn As SQLConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim hierDS As DataSet
Dim sqlCmdBldrDetail As SqlCommandBuilder
Dim orderRow As DataRow
Dim detailRow As DataRow
Try
"Створити новий об'єкт SQLConnection
sqlConn = New SqlConnection(Common.getConnectionString)
"Створити новий об'єкт SQLDataAdapter для таблиці Order
sqlDAOrder = New SqlDataAdapter()
"Створити новий об'єкт SqlDataAdapter для таблиці OrderDetails
sqlDADetail = New SqlDataAdapter()
"Створити новий DataSet
hierDS = New DataSet()
"Створити новий об'єкт SQLCommandBuilder для автоматичної генерації
"Виразів Update
sqlCmdBldrDetail = New SqlCommandBuilder(sqlDADetail)
With sqlDAOrder
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.Text
. CommandText = "Exec GetOrderHeader @ OrderId =- 1"
.Connection = sqlConn
End With
"Додати об'єкт InsertCommand
.InsertCommand = New SqlCommand()
"Вказати команду Insert
With .InsertCommand
.CommandType = CommandType.StoredProcedure
.CommandText = "InsertOrderHeader"
.Connection = sqlConn
"Визначити параметри параметризованих запиту Insert
.Parameters.Add _
(New SqlParameter ("@ CustomerId", SqlDbType.Int))
"Поставити властивість Direction
. Parameters ("@ CustomerId"). Direction = ParameterDirection.Input
"Поставити властивість SourceColumn
. Parameters ("@ CustomerId"). SourceColumn = "CustomerId"
.Parameters.Add _
(New SqlParameter ("@ OrderDate", SqlDbType.DateTime))
"Поставити властивість Direction
. Parameters ("@ OrderDate"). Direction = ParameterDirection.Input
"Поставити властивість SourceColumn
. Parameters ("@ OrderDate"). SourceColumn = "OrderDate"


.Parameters.Add _
(New SqlParameter ("@ OrderId", SqlDbType.Int))
"Поставити властивість Direction
. Parameters ("@ OrderId"). Direction = ParameterDirection.Output
"Поставити властивість SourceColumn
. Parameters ("@ OrderId"). SourceColumn = "OrderId"
End With
"Заповнити таблицю Orders даними
.Fill(hierDS, "Orders")
End With
With sqlDADetail
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.Text
. CommandText = "Exec GetOrderDetails @ OrderId =- 1"
.Connection = sqlConn
End With
"Заповнити таблицю Details даними
.Fill(hierDS, "Details")
End With
"Встановити зв'язок між таблицями
hierDS.Relations.Add("Order_Detail", _
hierDS.Tables ("Orders"). Columns ("OrderId"), _
hierDS.Tables ("Details"). Columns ("OrderId"))
"Створити новий рядок для таблиці Orders
orderRow = hierDS.Tables("Orders").NewRow()
"Вказати значення кожного стовпця в таблиці Orders
orderRow.Item("CustomerId") = 1
orderRow.Item("OrderStatus") = 400
orderRow.Item("OrderDate") = Now()

"Додати рядок до DataSet
hierDS.Tables("Orders").Rows.Add(orderRow)
"Синхронізувати зміни з джерелом даних
sqlDAOrder.Update(hierDS, "Orders")
"Створити новий рядок для таблиці Details
detailRow = hierDS.Tables("Details").NewRow()
detailRow.Item ("OrderId") = orderRow.Item ("OrderId")
detailRow.Item("ItemId") = 13

"Додати рядок до DataSet
hierDS.Tables("Details").Rows.Add(detailRow)
"Синхронізувати зміни з джерелом даних
sqlDADetail.Update(hierDS, "Details")
Catch e As Exception
"Опрацювати виняток
Finally
"Виконати очищення
End Try


Зверніть увагу: якщо нові рядки, додані до дочірньої таблиці,
відповідають новому рядку, доданої до батьківської таблиці, то для
збереження цілісності даних спочатку оновлюється батьківська таблиця. Дочірня
рядок завжди повинна посилатися на допустиму рядок в батьківській таблиці.

Використання властивості InsertCommand


У цьому прикладі заголовок замовлення і його позиції передаються збереженій процедурі
у вигляді XML, що дозволяє виконати операцію за одне звернення до бази даних.

Щоб вказати власне висловлювання INSERT, Що виконується при виклику
методу Update стосовноDataAdapter, Слід задати властивість
InsertCommand. Значенням цієї властивості може бути параметризованих
запит або збережена процедура. Параметри InsertCommand визначаються
так само, як і параметри об'єкта Command. Керований провайдер SQL
підтримує іменовані параметри.

Для кожного параметра потрібно встановити властивість SourceColumn. Воно
повідомляє об'єкту DataAdapter, В якому стовпці таблиці міститься значення
параметра.

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim hierDS As DataSet
Try
"Створити нове з'єднання
sqlConn = New SqlConnection(Common.getConnectionString)
"Створити новий об'єкт SqlDataAdapter для таблиці Order
sqlDAOrder = New SqlDataAdapter()
"Створити новий об'єкт SqlDataAdapter для таблиці OrderDetails
sqlDADetail = New SqlDataAdapter()
"Створити новий DataSet
hierDS = New DataSet()
With sqlDAOrder
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.Text
. CommandText = "Exec GetOrderHeader @ OrderId =- 1"
.Connection = sqlConn
End With
"Додати об'єкт InsertCommand
.InsertCommand = New SqlCommand()
"Вказати команду Insert
With .InsertCommand
.CommandType = CommandType.StoredProcedure
.CommandText = "InsertOrder"
.Connection = sqlConn
"Поставити параметри параметризованих вираження Insert
.Parameters.Add _
(New SqlParameter ("@ Order", SqlDbType.NVarChar, 4000))
"Встановити властивість Direction
. Parameters ("@ Order"). Direction = ParameterDirection.Input
.Parameters.Add _
(New SqlParameter ("@ OrderId", SqlDbType.Int))
"Встановити властивість Direction
. Parameters ("@ OrderId"). Direction = ParameterDirection.Output
"Встановити властивість SourceColumn
. Parameters ("@ OrderId"). SourceColumn = "OrderId"
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Orders")
End With
With sqlDADetail
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду Select для об'єкта sqlDADetail
With .SelectCommand
.CommandType = CommandType.Text
. CommandText = "Exec GetOrderDetails @ OrderId =- 1"
.Connection = sqlConn
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Details")
End With
"Встановити між двома таблицями ставлення через зовнішній ключ
hierDS.Relations.Add("Order_Detail", _
hierDS.Tables ("Orders"). Columns ("OrderId"), _
hierDS.Tables ("Details"). Columns ("OrderId"))
"Створити новий рядок у таблиці Orders
orderRow = hierDS.Tables("Orders").NewRow()
"Поставити значення кожного стовпця в таблиці Orders
orderRow.Item("OrderId") = -1
orderRow.Item("CustomerId") = 1
orderRow.Item("OrderStatus") = 400

"Додати рядок до DataSet
hierDataSet.Tables("Orders").Rows.Add(orderRow)
"Створити новий рядок у таблиці Details
detailRow = hierDataSet.Tables("Details").NewRow()
detailRow.Item ("OrderId") = orderRow.Item ("OrderId")
detailRow.Item("ItemId") = 13

"Додати рядок до DataSet
hierDataSet.Tables("Details").Rows.Add(detailRow)
"Створити новий рядок у таблиці Details
detailRow = hierDataSet.Tables("Details").NewRow()
detailRow.Item ("OrderId") = orderRow.Item ("OrderId")
detailRow.Item("ItemId") = 12

"Додати рядок до DataSet
hierDataSet.Tables("Details").Rows.Add(detailRow)
sqlDAOrder.InsertCommand.Parameters ("@ Order"). Value = _
hierDataSet.GetXml()
"Синхронізувати зміни з джерелом даних
sqlDAOrder.Update(hierDataSet, "Orders")
Catch E As Exception
"Опрацювати винятку
Finally
"Виконати очищення
End Try


Оновлення


При оновлення рядків у зв'язаних таблицях повинна зберігатися цілісність
даних. Для забезпечення посилальної цілісності дочірня рядок повинен посилатися
на допустиму рядок в батьківській таблиці. При цьому можна використовувати
каскадне оновлення.

Автоматична генерація команди Update


Зміни передаються джерела даних після оновлення рядка в таблиці
об'єкта DataSet і виклику методу Update об'єкта DataAdapter.
Останній автоматично генерує команду Update на основі
наданої вами команди Select.

У наступному прикладі коду ми переносимо позиції з одного замовлення в іншій,
демонструючи, як здійснюється каскадне оновлення.

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim sqlCmdBldrDetail As SqlCommandBuilder
Dim hierDS As DataSet
Try
"Створити нове з'єднання
sqlConn = New SqlConnection(Common.getConnectionString)
"Створити новий об'єкт SqlDataAdapter для таблиці Order
sqlDAOrder = New SqlDataAdapter()
"Створити новий об'єкт SqlDataAdapter для таблиці OrderDetails
sqlDADetail = New SqlDataAdapter()
"Створити новий DataSet
hierDS = New DataSet()
"Створити новий об'єкт SQLCommandBuilder, автоматично формує
"Команди Update
sqlCmdBldrDetail = New SqlCommandBuilder(sqlDADetail)
With sqlDAOrder
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.Text
.CommandText = "Exec GetOrderHeader @OrderId=2"
.Connection = sqlConn
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Orders")
End With
With sqlDADetail
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.Text
. CommandText = "Exec GetOrderDetails @ OrderId = 2"
.Connection = sqlConn
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Details")
End With
"Встановити між двома таблицями ставлення через зовнішній ключ
hierDS.Relations.Add("Order_Detail", _
hierDS.Tables ("Orders"). Columns ("OrderId"), _
hierDS.Tables ("Details"). Columns ("OrderId"))
hierDS.Tables ("Orders"). Columns ("OrderId"). ReadOnly = False
"Перенести позиції з одного замовлення в іншій
orderRow = hierDataSet.Tables("Orders").Rows(0)
orderRow("OrderId") = 1
"Синхронізувати зміни
sqlDADetail.Update(hierDS, "Details")
Catch E As Exception
"Опрацювати виняток
Finally
"Виконати очищення
End Try


Використання властивості UpdateCommand


Автоматично сформована команда носила кожен рядок за одне
звернення до бази даних, а збережена процедура могла б перемістити за одне
звернення всі рядки. Щоб вказати власне висловлювання Update,
виконується при виклику методу Update стосовноDataAdapter,
задайте властивість UpdateCommand. Його значенням може бути
параметризованих запит або збережена процедура. Параметри
UpdateCommand визначаються так само, як і параметри об'єкта
Command.

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim hierDS As DataSet
Try
"Створити нове з'єднання
sqlConn = New SqlConnection(Common.getConnectionString)
"Створити новий об'єкт SqlDataAdapter для таблиці Order
sqlDAOrder = New SqlDataAdapter()
"Створити новий об'єкт SqlDataAdapter для таблиці OrderDetails
sqlDADetail = New SqlDataAdapter()
"Створити новий DataSet
hierDS = New DataSet()
With sqlDAOrder
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.Text
. CommandText = "Exec GetOrderHeader @ OrderId = 1"
.Connection = sqlConn
End With
"Додати об'єкт UpdateCommand
.UpdateCommand = New SqlCommand()
"Вказати команду Update
With .UpdateCommand
.CommandType = CommandType.StoredProcedure
.CommandText = "MoveOrderDetails"
"Вказати параметри
. Parameters.Add (New SqlParameter ("@ FromOrderId", SqlDbType.Int))
. Parameters ("@ FromOrderId"). Direction = ParameterDirection.Input
. Parameters ("@ FromOrderId"). SourceColumn = "OrderId"
. Parameters ("@ FromOrderId"). SourceVersion = _
DataRowVersion.Original
. Parameters.Add (New SqlParameter ("@ ToOrderId", SqlDbType.Int))
. Parameters ("@ ToOrderId"). Direction = ParameterDirection.Input
. Parameters ("@ ToOrderId"). SourceColumn = "OrderId"
. Parameters ("@ ToOrderId"). SourceVersion = DataRowVersion.Current
.Connection = sqlConn
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Orders")
End With
With sqlDADetail
"Додати об'єкт SelectCommand
sqlDADetail.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.Text
. CommandText = "Exec GetOrderDetails @ OrderId = 1"
.Connection = sqlConn
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Details")
End With
"Встановити між двома таблицями ставлення через зовнішній ключ
hierDataSet.Relations.Add("Order_Detail", _
hierDS.Tables ("Orders"). Columns ("OrderId"), _
hierDS.Tables ("Details"). Columns ("OrderId"))
hierDS.Tables ("Orders"). Columns ("OrderId"). ReadOnly = False
"Перенести позиції з одного замовлення в іншій
orderRow = hierDS.Tables("Orders").Rows(0)
orderRow("OrderId") = 2
"Синхронізувати зміни
sqlDAOrder.Update(hierDS, "Orders")
Catch E As Exception
"Опрацювати виняток
Finally
"Виконати очищення
End Try


Властивість SourceVersion дозволяє передавати оригінал і поточне
значення OrderId у відповідні параметри збереженої процедури.

Видалення


Видаляючи ієрархічні дані, ви повинні подбати про збереження цілісності
бази даних. Кожна дочірня рядок повинен посилатися на допустиму рядок
в батьківській таблиці. Тому видаляти рядок з батьківської таблиці не можна
до тих пір, поки існує хоча б одна пов'язана з нею рядок у дочірній
таблиці.

У ADO.NET об'єкт DataSet підтримує каскадне видалення, яке
дозволяє видаляти дочірні рядки одночасно з відповідними батьківськими
рядками. Щоб визначити дії, необхідні при будь-яких
зміни рядків у таблицях, ви повинні вказати обмеження зовнішнього ключа для
двох таблиць вDataSet. Налаштуйте метод DeleteProperty властивості
ForeignKeyConstraint на один з відповідних режимів роботи (за замовчуванням
операції виконуються в каскадному режимі).

Автоматично генерується команда Delete

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDADetail As SqlDataAdapter
Dim hierDS As DataSet
Dim sqlCmdBldrOrder As SqlCommandBuilder
Dim sqlCmdBldrDetail As SqlCommandBuilder
Try
"Створити новий SQLConnection
sqlConn = New SqlConnection(Common.getConnectionString)
"Створити новий об'єкт SqlDataAdapter для таблиці Order
sqlDAOrder = New SqlDataAdapter()
"Створити новий об'єкт SqlDataAdapter для таблиці OrderDetails
sqlDADetail = New SqlDataAdapter()
"Створити новий об'єкт DataSet
hierDS = New DataSet()
"Створити об'єкти CommandBuilder, автоматично формують команди
sqlCmdBldrOrder = New SqlCommandBuilder(sqlDAOrder)
sqlCmdBldrDetail = New SqlCommandBuilder(sqlDADetail)
With sqlDAOrder
"Додати об'єкт SelectCommand
sqlDAOrder.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.StoredProcedure
.CommandText = "GetOrderHeaders"
.Connection = sqlConn
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Orders")
End With
With sqlDADetail
"Додати об'єкт SelectCommand
sqlDADetail.SelectCommand = New SqlCommand()
"Вказати команду Select для об'єкта sqlDADetail
With .SelectCommand
.CommandText = "Select * from OrderDetails"
.Connection = sqlConn
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Details")
End With
"Встановити зв'язок між таблицями
hierDS.Relations.Add("Order_Detail", _
hierDS.Tables ("Orders"). Columns ("OrderId"), _
hierDS.Tables ("Details"). Columns ("OrderId"))
"Знайти останній рядок у таблиці Orders
orderRow = hierDS.Tables("Orders").Rows. _
Item(hierDS.Tables("Orders").Rows.Count – 1)
"Видалити рядок з таблиці Orders. У результаті цього
"Автоматично видаляються відповідні дочірні рядки
"З таблиці Details в DataSet.
orderRow.Delete()

"Синхронізувати зміни з джерелом даних
sqlDADetail.Update(hierDS, "Details")
sqlDAOrder.Update(hierDS, "Orders")
Catch E As Exception
"Опрацювати виняток
Finally
"Виконати очищення
End Try


Для підтримки цілісності даних дочірні рядки повинні видалятися першими.
Видалити батьківські рядка до видалення відповідних дочірніх рядків не можна.
Тому оновлення DataAdapter-об'єкта sqlDADetail для
таблиці Details виконується до поновлення DataAdapter-об'єкта
sqlDAOrder.

Використання властивості DeleteCommand


Щоб вказати власне висловлювання Delete, Що виконується при виклику
методу Update стосовноDataAdapter, Використовуйте властивість
DeleteCommand. Його значенням є параметризованих запит або
збережена процедура. Параметри для DeleteCommand визначаються так само, як
і для об'єкта Command.

Для кожного параметра потрібно задати властивість SourceColumn. Воно повідомляє
DataAdapter, В якому стовпці міститься значення параметра.

Dim sqlConn As SqlConnection
Dim sqlDAOrder As SqlDataAdapter
Dim sqlDaDetail As SqlDataAdapter
Dim hierDS As DataSet
Try
"Створити новий SQLConnection
sqlConn = New SqlConnection(Common.getConnectionString)
"Створити новий об'єкт SqlDataAdapter для таблиці Order
sqlDAOrder = New SqlDataAdapter()
"Створити новий об'єкт SqlDataAdapter для таблиці OrderDetails
sqlDaDetail = New SqlDataAdapter()
"Створити новий DataSet
hierDS = New DataSet()
With sqlDAOrder
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.StoredProcedure
.CommandText = "GetOrderHeaders"
.Connection = sqlConn
End With
"Додати об'єкт DeleteCommand
.DeleteCommand = New SqlCommand()
"Поставити властивості DeleteCommand
With .DeleteCommand
.CommandType = CommandType.StoredProcedure
.CommandText = "DeleteOrder"
.Connection = sqlConn
"Визначити параметри збереженої процедури
. Parameters.Add (New SqlParameter ("@ OrderId", SqlDbType.Int))
"Налаштувати властивість SourceColumn
. Parameters ("@ OrderId"). SourceColumn = "OrderId"
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Orders")
End With
With sqlDaDetail
"Додати об'єкт SelectCommand
.SelectCommand = New SqlCommand()
"Вказати команду Select
With .SelectCommand
.CommandType = CommandType.Text
.CommandText = "Select * from OrderDetails"
.Connection = sqlConn
End With
"Заповнити DataSet повернутими даними
.Fill(hierDS, "Details")
End With
"Встановити зв'язок між двома таблицями

"Знайти останній рядок у таблиці Orders
orderRow = hierDS.Tables("Orders").Rows. _
Item(hierDS.Tables("Orders").Rows.Count – 1)
"Видалити рядок з таблиці Orders. У результаті автоматично
"Видаляються відповідні дочірні рядки з таблиці
"Details в DataSet.
orderRow.Delete()

"Синхронізувати зміни з джерелом даних
sqlDAOrder.Update(hierDS, "Orders")
Catch E As Exception
"Опрацювати виняток
Finally
"Виконати очищення
End Try


Висновок


При доступі до даних лише для читання об'єкти DataReader
іXmlReader прості у використанні і працюють швидко, хоча з'єднання
з базою даних підтримується до тих пір, поки додаток читає дані. Якщо
додаток досить довго тримає ці об'єкти, може виникнути конкуренція
(Contention), що обмежить масштабованість такого додатка.

Об'єкт DataSet представляє від'єднаний реляційний кеш, а також
спрощує навігацію по ієрархічним даними і їх модифікацію. Каскадна запис
полегшує фіксацію змін у базі даних, але автоматично генеруються
вираження Insert, Update і Delete менш ефективні в порівнянні з тими, які
пишуться вручну, – особливо з точки зору зменшення частоти звернень до бази
даних та кешування запитів. Скорочення числа звернень до бази даних стане
ще актуальніше, коли XML-засоби SQL Server 2000 стануть повніше
використовувати можливості ADO.NET.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*