Тема 3. «Наборы данных»

реклама
Тема 3. «Наборы данных»
Цель
Рассмотреть технологию передачи данных из источника данных в
приложение и обратно.
Задачи
1. Познакомиться с технологией передачи данных от источника
данных в приложение и обратно.
2. Рассмотреть свойства и методы адаптера данных (объект
DataAdapter в концепции ADO.NET).
3. Познакомиться со свойствами и методами объекта DataView.
Оглавление
Формирование наборов данных с помощью адаптеров данных
Свойства адаптеров данных
Методы адаптеров данных
Свойства и методы объекта DataView
Выводы
Вопросы для самопроверки
Литература
Формирование наборов данных с помощью адаптеров данных
В теме 1 вы познакомились с центральными компонентами
провайдера данных, который представляет собой подсистему модели
ADO.NET, обеспечивающую взаимодействие между приложением и
источником данных. Мы выяснили, что одним из компонентов .NETпровайдера данных является объект DataAdapter (рис. 3.1). В этой
теме мы подробно рассмотрим адаптеры данных и их применение.
Уровень
пользовательского
интерфейса
Промежуточный уровень
Уровень данных
DataReader
DataAdapter
Хранилище данных
Command
Хранилище данных
DataSet
Connection
Рис. 3.1. Трехуровневая архитектура информационной системы
Обмен данными между источниками и наборами данных (объекты
DataSet в концепции ADO.NET) предполагает считывание данных из
источника в набор и запись изменений, произведенных в наборе
данных, в источник. Эти процессы осуществляются адаптером данных
(объект DataAdapter в концепции ADO.NET).
Адаптер данных обеспечивает обмен не только между набором
данных и базой данных, но также между набором данных и другими
источниками, такими, например, как Microsoft Exchange Server.
Один адаптер данных образует информационный канал для обмена
между таблицей источника данных и объектом DataTable. Если в
наборе данных присутствуют несколько объектов DataTable. То
имеется возможность применить столько же и адаптеров данных для
обмена данными с несколькими таблицами.
Чтобы заполнить набор данных информацией из таблицы источника,
следует вызвать метод адаптера данных. Этот метод выполняет SQLзапрос или вызывает хранимую процедуру для считывания данных из
источника и передачи их в соответствующую таблицу набора данных.
Для чтения данных адаптер создает объект DataReader (в фоновом
режиме, автоматически).
Метод адаптера данных также вызывается, когда вам нужно обновить
данные в источнике, записав в него изменения, накопленные в наборе
данных. При вызове метода также выполняется SQL-запрос или
вызывается хранимая процедура.
Адаптеру данных должна быть задана конфигурация либо при его
создании, либо на более поздних стадиях, но до его использования. В
конфигурации указываются данные для обмена и содержатся ссылки
на SQL-операторы (или хранимые процедуры), предназначенные для
выполнения чтения/записи источника данных.
Свойства адаптеров данных
Операции с источником данных, доступные адаптеру данных,
включают в себя чтение, добавление и удаление записей; имеется
возможность настроить способ выполнения этих операций.
Перечисленные операции представлены следующими свойствами
адаптера (рис. 3.2):
 SelectCommand – чтение данных из источника;
 InsertCommand – добавление данных к источнику;
 UpdateCommand – перезапись существующих данных;
 DeleteCommand – удаление данных.
Еще одно важное свойство адаптера данных– TableMappings
содержит коллекцию, составленную из отображений исходных таблиц
базы данных на объекты DataTable набора данных. По умолчанию
свойство TableMappings представляет собой пустую коллекцию. Для
указания информативных имен таблиц, помещаемых в набор данных,
и установления соответствия с именами аналогичных таблиц в
источнике данных используется коллекция объектов DataTableMapping
и метод Add, например:
daDep.TableMappings.Add("Table", "Отделы")
daDep.TableMappings.Add("Table1", "Сотрудники")
SQL
Server
Подключение
Оператор
SQL
(SELECT)
Оператор
SQL
(INSERT)
Оператор
SQL
(UPDATE)
Оператор
SQL
(DELETE)
SQLDataAdapter
Объект DataSet
(Набор данных)
Рис. 3.2. Схема работы объекта DataAdapter как связующего звена
между источником данных и набором данных
Свойства
адаптера
данных
SelectCommand,
InsertCommand,
UpdateCommand, DeleteCommand являются объектами Command и
поддерживают свойство CommandText, которое ссылается на команду
SQL или хранимую процедуру.
При создании экземпляра объекта SqlDataAdapter можно определять
параметры подключения и используемые операторы в свойстве
SelectCommand. Параметры подключения могут быть заданы через
объект ConnectionString или сам объект Connection.
Dim da As New SqlDataAdapter
Dim cnn As New SqlConnection("server=Persist Security Info=False;
Integrated Security=SSPI; database=HumanResources; server=abrzh")
da.SelectCommand = New SqlCommand
da.SelectCommand.Connection = cnn
da.SelectCommand.CommandText = "SELECT EmployeeID, FirstName"
Существует
другой
способ
создания
объекта
DataAdapter
перегруженным
конструктором.
Не
обязательно
создавать
подключение заранее, до инициализации объекта DataAdapter,
поскольку ConnectionString все равно передается в момент создания
объекта DataAdapter:
Dim da As SqlDataAdapter = New SqlDataAdapter ("SELECT * FROM
Departments", "server=Persist Security Info=False;Integrated
Security=SSPI; database=EmployeeInfo; server=abrzh")
Различия между этими двумя методиками касается закрытия
подключения. В первом сценарии, когда подключение уже существует,
обязанности по закрытию подключения возлагаются на приложение.
При использовании второй методики, подключение разрывается
автоматически, сразу после выполнения оператора T-SQL.
Методы адаптеров данных
Для того чтобы извлекать данные из таблиц SQL Server после
начальной
настройки
свойства
SelectCommand
объекта
SqlDataAdapter вам понадобится перемещать данные в набор данных
(объект Dtaset). При выполнении метода Fill объекта SqlDataAdapter
производится заполнение DataSet и, соответственно, базового
объекта DataTable записями, полученными от источника данных SQL
Server.
Dim da As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM
Departments", "server=Persist Security Info=False; Integrated
Security=SSPI; database=EmployeeInfo; server=abrzh")
Dim ds As New DataSet()
da.Fill(ds, "Departments")
Для отображения набора данных в форме можно использовать очень
удобный визуальный элемент DataGrid, перетащив его в форму из
панели управления. Необходимо выполнить связывание набора
данных с интерфейсным элементом:
DataGrid1.DataSource = ds
Обычно после внесения всех необходимых изменений в таблицы
набора данных DataSet потребуется сохранить эти изменения в
источнике данных. Для этого нужно вызвать метод Update объекта
DataAdapter, который анализирует изменения в указанной таблице
набора данных (или сразу во всех таблицах, если ни одна из них не
указана явно). Для каждой измененной записи по отношению к
источнику данных выполняется команда вставки, обновления или
удаления с помощью соответствующего объекта InsertCommand,
UpdateCommand или DeleteCommand.
Каждая измененная запись обновляется отдельно, а не как часть
транзакции или пакетной операции. Причем порядок обновления
записей определяется порядком их расположения в объекте
DataTable.
Для явного управления порядком выполнения операций для заданной
таблицы можно использовать метод GetChanges, который доступен
как на уровне объекта DataSet, так и на уровне объекта DataTable.
Этот метод используются для извлечения отдельных наборов записей
с разными состояниями записей.
Предположим, мы обновляем нашу базу данных EmployeeInfo
данными из объекта DataSet, передавая их в базу данных с помощью
объекта DataAdapter. Причем сначала требуется выполнить все
операции удаления, затем все обновления, а потом все вставки
записей. Это можно сделать, трижды вызывая метод GetChanges
объекта DataTable с указанием соответствующих разных состояний
записей (значения коллекции DataRowState). После каждого вызова
метода GetChanges вызывается метод Update объекта DataAdapter с
передачей объекта DataTable, возвращенного методом GetChanges.
Dim dt As New DataTable
dt = ds.Tables("Departments")
Dim tab As New DataTable
tab = dt.GetChanges(DataRowState.Deleted)
If Not tab Is Nothing Then
da.Update(tab)
End If
tab = dt.GetChanges(DataRowState.Modified)
If Not tab Is Nothing Then
da.Update(tab)
End If
tab = dt.GetChanges(DataRowState.Added)
If Not tab Is Nothing Then
da.Update(tab)
End If
Объект DataAdapter не создает автоматически команды INSERT,
UPDATE и DELETE для обновления источника данных в соответствии
с изменениями данных в объекте DataSet. Эти команды можно указать
различными
способами,
например,
использовать
объект
CommandBuilder для автоматической генерации команд во время
выполнения приложения, или явно запрограммировать эти команды,
или использовать компонент DataAdapter Design-Time Component и
мастер конфигурирования объекта DataAdapter Configuration Wizard.
Рассмотрим использование объекта CommandBuilder – это самый
простой способ выполнения команд INSERT, UPDATE и DELETE.
Следует иметь в виду, что объект CommandBuilder имеет некоторые
ограничения в использовании, так, например, он не подлежит
конфигурированию (настройке), предназначен только для одной
таблицы, не учитывает связи таблицы с другими таблицами.
Dim cb As New SqlCommandBuilder(da)
Этой строчки кода достаточно для того, чтобы созданный объект
CommandBuilder в фоновом режиме автоматически сгенерировал
команды INSERT, UPDATE и DELETE для указанного DataAdapter.
Как и следовало ожидать, для каждого типа провайдера данных .NET
используется специализированный объект: SqlCommandBuilder,
OleDbCommandBuilder, ObdcCommandBuilder.
При автоматической генерации команд объект CommandBuilder
использует схему таблицы, полученную с помощью метода select
объекта SelectCommand. Учтите, что поля, возвращаемые объектом
SelectCommand, должны содержать первичный ключ или поле с
уникальными значениями.
Ни один из перечисленных выше методов указания команд
обновления не позволяет обновлять данные сразу в нескольких
таблицах, особенно если они связаны родительско-дочерними
отношениями (как таблицы Departments и Employees на рис. 3.3). Но
это не означает, что концепция ADO.NET не позволяет разрешить
подобную ситуацию. Для доказательства этого утверждения
рассмотрим следующий программный код:
Dim cnn As New SqlConnection("Persist Security Info=False;Integrated
Security=SSPI;database=EmployeeInfo;server=abrzh")
Dim ds As DataSet
'Создание адаптеров данных
Dim daDep As New SqlDataAdapter("SELECT * FROM Departments", cnn)
Dim daEmp As New SqlDataAdapter("SELECT * FROM Employees", cnn)
'Создание объектов CommandBuilder
Dim cbDep As New SqlCommandBuilder(daDep)
Dim cbEmp As New SqlCommandBuilder(daEmp)
Чтение изменений в таблицах набора данных в "правильном" порядке
в соответствии с родительско-дочерними отношениями между
таблицами, установленными при их создании
Dim tab As New DataTable
'Чтение удаленных записей в дочерней таблице
tab = ds.Tables("Employees").GetChanges(DataRowState.Deleted)
If Not tab Is Nothing Then
daEmp.Update(tab)
End If
'Чтение всех измененных записей в родительской таблице.
tab = ds.Tables("Departments").GetChanges
If Not tab Is Nothing Then
daDep.Update(tab)
End If
'Чтение всех новых или измененных записей в дочерней таблице.
tab = ds.Tables("Employees").GetChanges(DataRowState.Added Or
DataRowState.Modified)
If Not tab Is Nothing Then
daEmp.Update(tab)
End If
End Sub
Departments
DepartmentsID
<pi> I
<M>
DepartmentsName
VA20
работает
Employees
EmployeesID <pi> I
<M>
Name
VA20
Рис. 3.3. Пример родительско-дочерних связей между таблицами
Таким образом, мы внесли в базу данных изменения данных в
объектах-таблицах с учетом родительско-дочерних связей между
ними.
К
сожалению,
ссылочная
целостность
данных
не
поддерживается автоматически, а потому ее нужно организовать
вручную. Для этого разработчику необходимо сгруппировать типы
изменений, а затем выполнить их в правильном порядке. Для двух
таблиц, между которыми существуют родительско-дочерние связи,
изменения следует вносить в приведенном порядке:
1. Сначала удалить записи в дочерней таблице (или таблице,
которая в реляционном отношении находится на стороне
«многие»).
2. Вставить, обновить и удалить записи в родительской таблице
(или таблице, которая в реляционном отношении находится на
стороне «один»).
3. Вставить и обновить записи в дочерней таблице (или таблице,
которая в реляционном отношении находится на стороне
«многие»).
Для чтения соответствующих изменений в наборе данных необходимо
вызвать для таблиц метод GetChanges с фильтром состояния записи.
Каждый вызов метода GetChanges возвращает объект DataTable с
измененными записями и заданным состоянием. Если таких записей
нет, то возвращается значение Nothing. Если есть хотя бы одна
измененная строка с заданным состоянием, то для фактического
обновления базы данных вызывается метод Update объекта
DataAdapter.
Свойства и методы объекта DataView
При
разработке
интерфейса
приложения
используются
дополнительные классы, например класс DataView, который является
пользовательским представлением данных.
Объекты класса DataView обладают набором свойств, которые
позволяют настраивать способ отображения данных из объекта
DataTable. Таким образом, представление данных с помощью
объектов DataView может быть подвергнуто сортировке (изменяется
порядок сортировки: нисходящий или восходящий) по одному или
нескольким полям, фильтрации (используется фильтр состояния
записей, который указывает критерии отображения записей на основе
состояния записи) и редактированию. Также, впоследствии вы
сможете осуществлять поиск и навигацию с использованием объектов
класса DataView.
Объект DataView – полностью динамическое представление данных,
то есть все изменения в таблице-источнике немедленно
отображаются в объекте DataView. Но существуют некоторые
особенности применения этого объекта. Каждый объект DataView
является представлением только одной таблицы и не может быть
объединением нескольких таблиц. Не смотря на то, что объект
DataView очень похож на таблицу, он не может использоваться как
таблица, в нем нельзя исключать поля, которые присутствуют в
таблице-источнике, не возможно включать дополнительные поля,
например, вычисляемые поля, которых нет в таблице-источнике.
Другими словами, объект DataView является представлением
таблицы, но не самой таблицей. У каждой таблицы может быть
сколько угодно представлений и для всех этих представлений таблица
является источником полей и строк. Данный класс можно считать
аналогией представления в базе данных.
Объекты DataView могут быть созданы двумя способами. Первый
способ представляет собой свойство DefaultView непосредственно
таблицы-источника. Предположим, нам нужно создать представление
для таблицы Employees базы данных EmployeeInfo, причем
сотрудники в этом представлении должны быть упорядочены по
номеру отдела и получать заработную плату более 10000 рублей. Для
этого нужно использовать следующий код:
dsEmployees.Tables("Employees").DefaultView.RowFilter=”Salary >
10000”
dsEmployees.Tables("Employees").DefaultView.Sort=”DepartmentID”
Если необходимо отобразить в представлении текущие значения
только тех записей, которые были изменены (но еще не сохранены),
то можно изменить настройки представления:
dsEmployees.Tables("Employees").DefaultView.RowFilter=” ”
dsEmployees.Tables("Employees").DefaultView.RowSatateFilter=
DefaultView.RowState.ModifiedCurrent
Второй способ создания представления для таблицы представляет
собой явное создание объекта класса DataView для конкретной
таблицы с последующей настройкой свойств (RowFilter, Sort и
RowStateFilter) объекта.
dvView2 = New DataView(dsEmployees.Tables("Employees"), "",
"FirstName", DataViewRowState.CurrentRows)
Этой кодовой строкой было создано представление для таблицы
Employees, в котором нет фильтра, сортировка по полю FirstName и
изображены текущие записи таблицы.
Это представление можно изменять, например, установить
фильтрацию и сортировку по убыванию:
dvView2.RowFilter=”FirstName > ‘Г’”
dvView2.Sort= “FirstName DESC”
dvView2.RowStateFilter=DataViewRowState.Current
Объект DataView имеет модель редактирования, аналогичную модели
редактирования объекта DataTable,. то есть в представлении можно
удалять, добавлять, редактировать данные.
С объектом класса DataView вы можете встретиться не только при
непосредственном создании этого объекта. Дело в том, что свойство
DefaultView объекта класса DataTable возвращает объект DataView,
содержащий объекты DataRow из DataTable. Причем на объект
DataView
могут
быть
наложены
определенные
условия,
определяющие строки DataRow, которые в результате будут
возвращены.
В следующем примере происходит извлечение объекта DataView при
помощи свойства DefaultView. Для него задан набор фильтров
RowFilter, определяющих, что в результирующем множестве должны
быть возвращены строки, в которых значение столбца lname
начинаются с буквы «А»:
Imports System.Data, System.Data.SqlClient
Dim cnn As New SqlConnection("Persist Security Info=False;Integrated
Security=SSPI;database=EmployeeInfo;server=abrzh")
Dim ds As DataSet
Dim iLoop As Integer
Dim da As New SqlDataAdapter(“SELECT * FROM Employee”, cnn)
da.Fill(ds)
Dim dv As DataView
Dim dvRow As DataRowView
dv=ds.Tables(“table”).DeaultView
dv.RowFilter=”lname LIKE ‘A%’ “
Таким образом, при просмотре записей объекта DataView мы увидим
только те строки, которые соответствуют заданному критерию:
For Each dvRow In dv
For iLoop=0 To dv.Table.Columns.Count-1
Console.Write(dvRow.Item(iLoop))
Console.Write(Chr(9))
Next
Console.WriteLine(“ “)
Next
В приведенном программном коде следует пояснить использование
незнакомого класса DataRowView. Свойство Item класса DataView
выступает в качестве индексатора для класса DataView. Свойство
возвращает объект класса DataRowView.
Кроме свойств и методов объекты класса DataView поддерживают
событие ListChanged. Это событие возникает в случае изменения
списка, управляемого объектом DataView.
Выводы
Мы рассмотрели объект DataAdapter и его роль в архитектуре
отсоединенных данных. Объект DataAdapter играет роль моста между
неподключенными объектами DataSet (наборами данных) и
подключенными объектами DataProvider, которые фактически связаны
и подключены к физическому источнику данных.
Объект DataAdapter используется для вставки данных в набор данных
(объект DataSet) из источника данных с помощью явно заданных
команд или хранимых процедур. Объект DataAdapter также
автоматически обновляет источник данных теми изменениями,
которые произошли в наборе данных (объекте DataSet), что позволяет
полностью настроить команды вставки, обновления и удаления для
источника данных.
Кроме базовых классов, обеспечивающих технологию доступа к
данным, в концепции .NET имеется ряд дополнительных классов,
упрощающих программирование доступа и манипулирования
данными. К таким классам можно отнести, например, класс
CommandBuilder (обеспечивает автоматическую генерацию команд
UPDATE, DELETE, INSERT для источника данных) и класс DataView
(являет собой пользовательское представление наших данных,
обеспечивает выполнение сортировки, фильтрации и навигации).
Вопросы для самопроверки
1. Опишите технологию доступа к источнику данных с помощью
адаптера данных? Какие методы объекта DataAdapter для этого
следует использовать?
2. С какими проблемами сталкивается разработчик при обновлении
источника данных информацией из набора данных.
3. Существует ли возможность установления соответствия имен
таблиц из набора данных и имен таблиц в источнике данных?
4. Поместите объект CommandBuilder в модель ADO.NET
(нарисуйте схему).
5. Объект CommandBuilder позволяет вносить изменения только в
одну таблицу источника данных. Каким образом с помощью этого
объекта выполнить обновления в связанных таблицах источника
данных?
6. Что представляет собой коллекция DataRowState, какие
значения она содержит, какому объекту принадлежит?
Приведите пример использования коллекции DataRowState.
7. Можно ли считать класс объектов DataView аналогией обычного
представления данных в реляционных базах данных?
Литература
1. Т. Бэйн, Д. Госнелл, Дж. Уолш. Visual Basic .NET и SQL Server
2000: эффективный уровень данных. / Пер. с англ.; Под ред.
С.М. Молявко. – М.: БИНОМ. Лаборатория знаний, 2006. – 604 с.,
ил.
2. Рохилла С., Натан С., Мэлхотра С. Microsoft ADO.NET:
разработка профессиональных проектов. – СПб.: БХВПетербург, 2005. – 789 с.: ил.
3. Эпплман Д. Переход на VB.NET: стратегии, концепции, код. —
СПб.: Питер, 2002. — 464 с.: ил.
4. Троелсен Э. C# и платформа .NET. Библиотека программиста —
СПб.: Питер, 2006 — 796 с.: ил.
Скачать