|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ГЛАВА 5ADO.NET: объект DataSet Объект DataSet является центральным и наиболее революционным элементом модели доступа к данным ADO.NET. По сути, это кэш-область в оперативной памяти для данных из одного или нескольких источников. Его можно представить как полноценную базу данных, которая полностью находится в оперативной памяти. Вероятно, наиболее важной характеристикой объекта DataSet является его использование в отключенном режиме. Описанные в главе 4, "Модель ADO.NET: провайдеры данных", управляемые объекты провайдеров данных предоставляют функции, необходимые при физическом подключении к базе данных или другому источнику данных. А объект DataSet и связанные с ним объекты (DataTable, DataRow, DataColumn и DataRelation) предлагают богатую функциональность в отключенном от источника данных режиме работы. Еще одной ключевой характеристикой объекта DataSet является независимость загруженных данных от их источника. Он содержит данные и позволяет манипулировать ими в реляционной манере. Поэтому DataSet и подчиненные ему объекты имеют универсальный способ применения и не связаны с каким-то отдельным провайдером данных. Таким образом, существует только один объект DataSet и не существует никаких других специализированных объектов DataSet, например SqlDataSet, OledbDataSet или OdbcDataSet. Если объекту DataSet не известен источник данных, то как же данные загружаются в него и как изменения данных передаются источнику? Ответ на эти вопросы можно получить, познакомившись с объектом DataAdapter, который служит мостом между объектом DataSet и физическим источником данных. Объект DataAdapter содержит специальные команды чтения данных из источника данных, а также команды обновления, удаления и вставки данных в источнике. Более подробно он рассматривается в главе 6, "ADO.NET: объект DataAdapter". Компоненты объекта DataSetОбъект DataSet является ключевым объектом ADO.NET и служит универсальным контейнером данных, независимо от используемого источника данных. Объект DataSet и связанные с ним подчиненные объекты предлагают реляционное представление данных, хотя он также способен загружать и сохранять свои данные в формате XML. Объект DataSet предлагает явную модель хранения данных в оперативной памяти, которая существует в полностью отключенном от источника данных состоянии и может легко передаваться между разными адресными пространствами и компьютерами. Ниже перечислены возможности, которыми обладает объект DataSet. • Хранение данных приложения. Объект DataSet может легко и гибко использоваться для хранения локальных данных приложения. Доступ к данным так же прост, как и доступ к данным в массиве, но объект DataSet предлагает дополнительные функции, например для сортировки и фильтрации, • Использование удаленных данных. Объект DataSet автоматически использует формат XML для маршалинга данных (т.е. их передачи от одного компьютера к другому). Эта возможность существен но упрощает разработку приложений на основе служб, SOAP или удаленного доступа к данным на более низком уровне. • Кэширование данных. Объект DataSet может кэшировать данные во время создания распределенных приложений на основе ASP.NET или других технологий, сокращая объем передаваемых по сети данных. • Устойчивое хранение данных. Объект DataSet предлагает методы сохранения своих данных и информации о схеме данных в стандартном формате XML. • Взаимодействие с пользователями. Объект DataSet эффективно поддерживает взаимодействие с пользователями для разных графических интерфейсов пользователя, комбинируя функции сортировки, фильтрации и прокрутки с возможностями их связывания с разными представлениями данных на основе Windows Forms и Web Forms. Данные в объекте DataSet организованы в одной или нескольких объектах DataTable. Каждый объект DataTable существует независимо от источника данных, т.е. он всегда отключен от источника данных, как и DataTable. Объект DataTable всего лишь хранит несколько таблиц данных и предлагает возможности манипулирования, передачи или связывания их с элементами управления пользовательского интерфейса. На рис. 5.1 показана схема взаимодействия объекта DataSet и связанных с ним подчиненных объектов. РИС. 5.1. Схема взаимодействия объекта DataSet с подчиненными объектами Объекты, которые являются подчиненными по отношению к объекту DataSet, обладают перечисленными ниже возможностями. • Объект DataTable содержит коллекции объектов DataRow, DataColumn и Constraint, а также коллекции объектов DataRelation, связанные с другими родительскими и дочерними объектами. Представление данных в этом объекте аналогично представлению данных в объекте Recordset в ADO 2.X. • Объект DataColumn является базовой единицей извлечения данных и определения схемы для объекта DataTable. Он содержит специализированную информацию для каждого поля объекта DataTable, включая имя, тип данных и другие атрибуты (например, свойства Unique, Readonly, AllowDBNull и Кроме того, он имеет свойство Expression для вычисления значения поля или создания итогового поля. • Объект DataRow представляет одну запись объекта DataTable и используется для добавления, извлечения и изменения данных в объекте DataTable. С его помощью можно осуществлять последовательный или непосредственный доступ к нужным записям объекта DataTable. • Объект DataRelation определяет отношение между двумя таблицами объекта DataSet. Он представляет классическое отношение между родительской и дочерней таблицей (т.е. между ключевым и внешним полями двух таблиц). Переходы между отношениями выполняются с помощью коллекций ChildRelations и ParentRelations (объектов DataRelation) объекта DataTable. • Объект Constraint определяет правило, согласно которому поддерживается целостность данных в объекте DataTable. Он содержит уже знакомое ограничение UniqueConstraint, гарантирующее уникальность значений таблицы, а также ограничение ForeignKeyConstraint, определяющее действия по ношению к строкам в связанной таблице. Ограничение может относиться к одному или нескольким объектам DataColumn. Каждый объект DataTable имеет свойство Constraint с коллекцией ограничений для данной таблицы. Ввод данных в объект DataSetДля ввода данных в таблицы DataTable объекта DataSet предусмотрены перечисленные ниже способы. 1. Программирование определений метаданных и прямая вставка данных. 2. Использование объекта DataAdapter для создания запроса по отношению к источнику данных. 3. Загрузка XML-документа. В этой главе представлен первый из перечисленных выше способов, в главе 6, "ADO.NET: объект DataAdapter", — второй, а в главе 10, "ADO.NET и XML", — третий. Этот раздел начинается с описания базовых функций объектов DataSet и DataTable. Учтите, что после загрузки данных в объект DataSet, использованный метод загрузки далее уже не имеет никакого значения, потому что все последующие операции с данными выполняются идентично. Определение схемы объекта DataTableДля иллюстрации основных принципов работы следует создать простую форму с помощью перечисленных ниже действий. 1. Запустите интегрированную среду разработки приложений Visual Studio .NET. 2. Создайте новый проект Visual Basic Windows Application. Для этого в диалоговом окне New Project (Новый проект) выберите тип проекта Visual Basic Project в области Project Types (Типы проектов), а затем шаблон Windows Application (Приложение Windows) в области Templates (Шаблоны). 3. Назовите проект DataSetCode. 4. Укажите путь к файлам проекта. 5. Увеличьте размер формы Form1. 6. В окне Properties укажите значение frmDataSets для свойства (Name) и значение DataSets для свойства Text формы Form1. 7. В верхнем левом углу формы создайте кнопку, перетаскивая ее из панели элементов управления. 8. В окне Properties укажите значение btnCreateDS для свойства (Name) и значение Create DataSet для свойства Text этой кнопки. 9. В правой части формы создайте поле со списком, перетаскивая его из панели элементов управления. 10. В окне Properties укажите значение lstOutput для свойства (Name). 11. Увеличьте размер поля со списком, чтобы оно занимало до 80% всей площади формы. В верхней части файла введите следующий код: Imports System Imports System.Data Затем в определении класса формы frmDataSets введите приведенный ниже код. Private dsEmployeelnfo As DataSet Private Sub btnCreateDS_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCreateDS.Click CreateDataSet() AddData() DisplayDataSet() End Sub Подпрограмма btnCreateDS_Click обработки щелчков на кнопке Create DataSet вызывает три другие подпрограммы для каждой фазы работы приложения. Переменная dsEmployeelnfo является объектом DataSet, доступ к которому выполняется с помощью подпрограмм внутри подпрограммы btnCreateDS_Click. НА ЗАМЕТКУ Итак, прежде всего нужно определить схему (или структуру) всех используемых таблиц. Для этого нужно определить все объекты DataColumn таблицы и указать их свойства, как показано на примере подпрограммы CreateDataSet () в листинге 5.1. ЛИСТИНГ 5.1. Код создания Объектов DataSet и DataTablePrivate Sub CreateDataSet() ' Создание объекта dsEmployeeInfo. dsEmployeelnfo = New DataSet() ' Создание таблицы Employees. Dim dtEmployees As DataTable = New DataTable("Employees") dtEmployees.CaseSensitive = False dtEmployees.Columns.Add("FirstName", Type.GetType("System.String")) dtEmployees.Columns.Add("LastName", Type.GetType("System.String")) dtEmployees.Columns.Add("DepartmentID", Type.GetType("System.Int32")) ' Вставка таблицы Employees в объект EmployeeInfo. dsEmployeeInfo.Tables.Add(dtEmployees) ' Создание таблицы Departments с ' помощью перегруженной версии конструктора. ' Это более длительный способ при создании стандартных полей, ' но он позволяет задавать другие свойства полей (например, Readonly & Unique) ' до включения поля DataColumn в коллекцию Columns. Dim dtDepartments As DataTable dtDepartments = New DataTable() dtDepartments.TableName = "Departments" dtDepartments.MinimumCapacity = 5 dtDepartments.CaseSensitive = False Dim NewColumn As New DataColumn() With NewColumn .ColumnName = "ID" .DataType = Type.GetType("System.Int32") .Readonly = True .Unique = True .AutoIncrement = True End With dtDepartments.Columns.Add(NewColumn) NewColumn = New DataColumn() With NewColumn .ColumnName = "DepartmentName" .DataType = Type.GetType("System.String") .Unique = True .AllowDBNull = False End With dtDepartments.Columns.Add(NewColumn) ' Включение таблицы Departments в объект dsEmployeeInfo. dsEmployeeInfo.Tables.Add(dtDepartments) End Sub После создания экземпляра dsEmployeeInfo объекта DataSet создается таблица Employees с помощью перегруженных конструкторов объекта DataTable с использованием параметра – имени таблицы. Затем задается значение False для свойства CaseSensitivity объекта DataTable. Это свойство определяет, будут ли операции сортировки, поиска и фильтрации выполняться с учетом регистра символов. По умолчанию значение этого свойства определяется как значение свойства CaseSensitivity родительского объекта DataSet или принимается равным False, если объект DataTable создан независимо от объекта DataSet. НА ЗАМЕТКУ Затем создаются определения полей с помощью метода Add объекта Column по указанному имени поля и типу данных. Учтите, что здесь указываются .NET-совместимые типы данных, а не используемые в базе данных типы. При отсутствии типа данных для него по умолчанию принимается строковый тип. Наконец, таблица Employee включается в объект dsEmployeeInfo. Далее этот процесс повторяется для таблицы Departments, но теперь уже с перегруженными конструкторами и функциями. Таким образом та же цель достигается другим способом. Разработчик может самостоятельно выбрать наиболее подходящий способ на основе собственного вкуса, корпоративных стандартов или особенностей поставленной задачи. Для свойства MinimumCapacity объекта dtDepartments задается значение 5, т.е. экземпляр объекта DataTable создается уже с пятью записями. Указание для него другого значения (отличного от используемого по умолчанию значения 25) позволяет управлять выделением ресурсов и оптимизировать производительность в критических ситуациях. Конечно, на самом деле эти пять записей фактически появятся только после того, как пользователь добавит их в таблицу DataTable, а пока для них резервируется место. Кроме того, перед добавлением полей в коллекцию Columns для них задаются значения других свойств. Перечисленные ниже свойства Readonly, Unique, AllowDBNull и AutoIncrement уже наверняка знакомы тем, кто имеет опыт создания приложений для работы с базами данных. Присвоение свойству Readonly значения True указывает на то, что значение поля нельзя изменить. Присвоение свойству Unique значения True указывает на то, что значения данного поля во всех записях таблицы должны быть уникальными. Это свойство реализуется с помощью автоматического создания ограничения UniqueConstraint для данного поля. Этот способ более подробно рассматривается в разделе об ограничениях таблицы далее в главе. Присвоение свойству AllowDBNull значения True указывает на то, что в данном поле допускается использование неопределенных значений. Присвоение свойству AutoIncrement значения True указывает на то, что значение поля увеличивается при каждом добавлении в таблицу новой записи. Для указания начального значения и приращения используются свойства AutoIncrementSeed и AutoIncrementStep. НА ЗАМЕТКУ Среди других свойств объекта DataColumn следует отметить MaxLength (для полей с данными типа String), DefaultValue и Table. Поле также можно определить с выражением для вычисления значения, создания итогового поля или фильтрования строк. Такое выражение может состоять из имен полей текущей записи или других записей, констант, операторов, символов подстановки, итоговых и других функций. Более подробную информацию и примеры таких выражений можно найти в справочных материалах для свойства Expression объекта DataColumn. Вставка данных в объект DataTableПосле определения объекта DataTable и его схемы можно начинать ввод данных. В листинге 5.2 приводится код вставки записей с данными в DataTable. Подпрограмма AddData включает четыре записи с данными в таблицу Departments и три записи с данными в таблицу Employees следующим образом. 1. Сначала создается новый экземпляр объекта DataRow для нужной таблицы с помощью метода NewRow. 2. Затем присваиваются значения полям этой записи. 3. После этого запись включается в коллекцию записей Rows таблицы с помощью метода Add свойства Rows таблицы. ЛИСТИНГ 5.2. Код программного ввода данных в объект DataTablePrivate Sub AddData () Dim dtDepartments As DataTable = dsEmployeeInfo.Tables ("Departments") Dim dtEmployees As DataTable = dsEmployeeInfo.Tables("Employees") ' Вставка четырех записей в таблицу Departments. Dim rowDept As DataRow rowDept = dtDepartments.NewRow rowDept("ID") = 11 rowDept("DepartmentName") = "Administration" dtDepartments.Rows.Add(rowDept) rowDept = dtDepartments.NewRow rowDept("ID") = 22 rowDept("DepartmentName") = "Engineering" dtDepartments.Rows.Add(rowDept) rowDept = dtDepartments.NewRow rowDept("ID") = 33 rowDept("DepartmentName") = "Sales" dtDepartments.Rows.Add(rowDept) rowDept = dtDepartments.NewRow rowDept("ID") =44 rowDept("DepartmentName") = "Marketing" dtDepartments.Rows.Add(rowDept) ' Вставка трех записей в таблицу Employees. Dim rowEmployee As DataRow rowEmployee = dtEmployees.NewRow rowEmployee("FirstName") = "Jackie" rowEmployee("LastName") = "Goldstein" rowEmployee("DepartmentID") = 22 dtEmployees.Rows.Add(rowEmployee) rowEmployee = dtEmployees.NewRow rowEmployee("FirstName") = "Jeffrey" rowEmployee("LastName") = "McManus" rowEmployee("DepartmentID") = 33 dtEmployees.Rows.Add(rowEmployee) rowEmployee = dtEmployees.NewRow rowEmployee("FirstName") = "Sam" rowEmployee("LastName") = "Johnson" rowEmployee("DepartmentID") = 33 dtEmployees.Rows.Add(rowEmployee) End Sub НА ЗАМЕТКУ Обновление данных в объекте DataSetДля обновления отдельной записи таблицы необходимо просто организовать доступ к нужной записи и присвоить новое значение одному из полей. Например, для изменения номера отдела, к которому относится Sam Johnson, можно использовать следующую строку кода: dtEmployees.Rows(2) ("DepartmentID") = 2 НА ЗАМЕТКУ Таким образом, в данные можно вносить произвольное количество изменений, но все они не будут зафиксированы до тех пор, пока не будет вызван метод AcceptChanges. Для отката изменений, внесенных после загрузки данных или последнего вызова метода AcceptChanges, используется метод RejectChanges. НА ЗАМЕТКУ Строки можно вставлять и удалять целиком. До сих пор был показан только процесс создания записей, а удаление записи основано на методе Remove объекта DataRowCollection (т.е. свойство Rows объекта DataTable). Этот метод полностью удаляет запись из коллекции. Другой способ удаления записи основан на методе Delete объекта DataRow. Этот метод отмечает запись для удаления, которое на самом деле произойдет только после вызова метода AcceptChanges. После вызова метода Remove все данные записи будут удалены необратимо, даже если после этого вызвать метод RejectChanges. Состояние и версия записиКаждый объект DataRow имеет свойство RowState, которое обозначает текущее состояние или статус записи. Кроме того, каждая запись хранит информацию о четырех разных версиях своего значения. По мере редактирования записи изменяется ее состояние и версия значения. В табл. 5.1 приведено краткое описание свойства RowState, а в табл. 5.2 – краткое описание свойства DataRowVersion. Таблица 5 1. Свойство RowState
Таблица 5.2. Свойство DataRowVersion
Если запись находится в состоянии Deleted в момент вызова метода AcceptChanges, то она удаляется из коллекции DataRowCollection. В противном случае версия записи Original обновляется версией Current, а состояние записи становится равным Unchanged. НА ЗАМЕТКУ Наоборот, если запись находится в состоянии Added в момент вызова метода RejectChanges, то запись удаляется из коллекции DataRowCollection. В противном случае версия записи Current обновляется версией Original, а состояние записи становится равным Unchanged. НА ЗАМЕТКУ Здесь следует особое внимание обратить на версию Proposed объекта DataRow. При вызове метода BeginEdit объекта DataRow обычные действия и события приостанавливаются, что позволяет пользователю вносить несколько изменений в запись, не применяя правил проверки вводимых значений. В этом режиме вносимые изменения отражаются не в версии Current, а в версии Proposed. И только после вызова метода EndEdit значения версии Proposed становятся (переносятся) значениями версии Current. Любые изменения можно отменить, вызывая метод CancelEdit до вызова метода EndEdit. Учтите, что изменения будут зафиксированы только после вызова метода AcceptChanges. Обработка ошибок ввода данных в записи и поляВ модели ADO.NET предусмотрен гибкий механизм определения и обработки ошибок ввода данных в записи и поля объекта DataTable. Этот механизм позволяет использовать в приложении правила проверки вводимых данных, сообщать об обнаруженных ошибках, но откладывать их исправление до определенного момента в потоке выполнения приложения. (Не путайте их с обычными системными исключительными ситуациями времени выполнения, которые обрабатываются стандартными конструкциями платформы.NET Framework на основе операторов Try-Catch-Finally.) Если приложение обнаруживает ошибку ввода данных, то оно создает описание найденной ошибки для записи или отдельного поля. Для обозначения наличия ошибки в отдельной записи используется свойство RowError объекта DataRow, например ему присваивается строка с комментарием "Something wrong here" (Здесь произошла какая-то ошибка). myDataRow.RowError = "Something wrong here" А для обозначения наличия ошибки в отдельном поле используется метод SetColumnError объекта DataRow, например с его помощью присваивается строка с комментарием "Bad data in this column" (Неверные данные в этом поле). myDataRow.SetColumnError (2, "Bad data in this column") Для извлечения строк с сообщениями о таких ошибках в полях или записях можно использовать свойство RowError или вызвать метод GetColumnError. Для сброса значения свойства RowError нужно присвоить ему пустую строку (" ") или использовать метод ClearErrors объекта RowError, который очищает свойство RowError и удаляет все сообщения об ошибках, заданные методом SetColumnError. Объект DataRow содержит свойство HasErrors, которое имеет значение True, если в поле или записи обнаружены какие-либо ошибки. Значение этого свойства отображается на свойство HasErrors объекта DataTable, которое при наличии ошибок в поле или записи также равно True. Аналогично, если свойство HasErrors для любой таблицы DataTable объекта DataSet имеет значение True, то значение свойства HasErrors объекта DataSet также равно True. Метод GetErrors объекта DataTable возвращает массив объектов DataRow, которые содержат ошибки. В целом этот простой механизм позволяет быстро определить любые ошибки ввода данных, как показано в листинге 5.3. Листинг 5.3. Пример обнаружения ошибок во всех таблицах объекта DataSetPrivate Sub ResolveErrors(myDataSet as DataSet) Dim rowsWithErrors() As DataRow Dim myTable As DataTable Dim myCol As DataColumn Dim currRow As Integer For Each myTable In myDataSet.Tables If myTable.HasErrors Then ' Извлечение всех записей с ошибками. RowsWithErrors = myTable.GetErrors() For currRow = 0 To rowsWithErrors.GetUpper For Each myCol In myTable.Columns ' Найти поля с ошибками и выбрать ' способ их обработки. ' Ошибка в поле извлекается с помощью метода ' rowsWithErrors(currRow).GetColumnError(myCol) Next ' очистка ошибок. rowsWithErrors(currRow).ClearErrors Next currRow End If Next End Sub Доступ к данным с помощью объекта DataTableПоскольку объект DataSet и содержащийся в нем объект DataTable всегда наполнены данными и не подключены к источнику данных, метод доступа к записям данных в них существенно отличается от методов доступа в ADO и других моделях доступа к данным (например, ODBC, DAO или RDO). Поскольку все данные доступны одновременно, в модели ADO.NET не существует понятия текущей записи. Поэтому нет никаких свойств или методов для перемещения от одной записи к другой. Каждый объект DataTable имеет свойство Rows, которое является набором объектов DataRow. Доступ к отдельному объекту осуществляется с помощью индекса или оператора For Each. Таким образом, в модели ADO.NET предлагается более простой и эффективный способ доступа и перемещения, аналогичный доступу к элементам массива. В листинге 5.4 показан код подпрограммы DisplayDataSet, которая отображает содержимое ранее созданных таблиц с загруженными в них данными. В ней применяется циклический обход всех элементов коллекции, т.е. коллекций Rows и Columns, для отображения содержимого таблицы Employees. Далее используется альтернативный метод доступа к записям и полям с помощью числового индекса для отображения содержимого таблицы Departments. ЛИСТИНГ 5.4. Код отображения данных в объектах DataTablePrivate Sub DisplayDataSet() Dim dr As DataRow Dim dc As DataColumn Me.lstOutput.Items.Add("DISPLAY DATASET") Me.lstOutput.Items.Add("============") ' Отображение данных из таблицы Employees. For Each dr In dsEmployeeInfo.Tables("Employees").Rows For Each dc In _ dsEmployeeInfo.Tables("Employees").Columns Me.lstOutput.Items.Add( _ dc.ColumnName & ": " & dr(dc)) Next Me.lstOutput.Items.Add ("============") Next Me.lstOutput.Items.Add("") ' Отображение данных из таблицы Departments. ' Пример использования индексов вместо оператора For Each. Dim row As Integer Dim col As Integer For row = 0 To dsEmployeeInfo.Tables("Departments").Rows.Count – 1 For col = 0 To dsEmployeeInfo.Tables("Departments").Columns.Count – 1 Me.lstOutput.Items.Add( _ dsEmployeeInfo.Tables("Departments").Columns(col).ColumnName & ":" & _ dsEmployeeInfo.Tables("Departments").Rows(row)(col)) Next col Me.lstOutput.Items.Add("============") Next row End Sub Аналогично можно создать подпрограмму более общего типа для обхода не только записей и полей, но и таблиц объекта DataSet, как показано в листинге 5.5. ЛИСТИНГ 5.5. Код обхода таблиц из объекта DataSetPrivate Sub DisplayDataSet(ByVal ds As DataSet) ' Общая подпрограмма для отображения содержимого объекта DataSet. ' Отображаемый объект DataSet передается как параметр. Dim dt As DataTable Dim dr As DataRow Dim dc As DataColumn Me.lstOutput.Items.Add("DISPLAY DATASET") Me.lstOutput.Items.Add("============") For Each dt In ds.Tables Me.lstOutput.Items.Add(") Me. lstOutput.Items.Add("TABLE: " & dt.TableName) Me.lstOutput.Items.Add(" ") For Each dr In dt. Rows For Each dc In dt.Columns Me.lstOutput.Items.Add(dc.ColumnName S ": " & dr(dc)) Next Me.lstOutput.Items.Add ("============") Next Next dt End Sub Обратите внимание, что здесь перегружается уже упомянутый ранее метод DisplayDataSet, который теперь принимает в качестве параметра объект DataSet. Попробуйте запустить полученное приложение; для этого введите упомянутый ранее код в проект DataSetCode и щелкните на кнопке Create DataSet. В результате этого действия создается объект DataSet и наполняется данными, которые затем отображаются в текстовом поле формы, как показано на рис. 5.2. РИС. 5.2. Результат создания объекта DataSet с таблицами Employees и Departments, наполнения их данными и последующего отображения НА ЗАМЕТКУ Поиск, фильтрация и сортировка записейИногда нужно работать не со всеми, а только с некоторыми записями объекта DataSet, например с одной записью или подмножеством всех записей. Для этого можно использовать методы Find и Select. Метод Find принадлежит свойству DataRowCollection объекта DataTable, который используется для поиска и возвращения единственной строки, указанной с помощью значения первичного ключа таблицы. Перед использованием метода Find для обнаружения некоторой строки в таблице Departments, которая определена в листинге 5.1, нужно определить первичный ключ таблицы. Это можно сделать с помощью присвоения одного или нескольких полей свойству PrimaryKey таблицы. (Даже если первичный ключ создан на основе единственного поля, свойство PrimaryKey таблицы является массивом объектов DataColumn.) В приведенных ниже строках кода, которые следует добавить в конце подпрограммы CreateDataSet из листинга 5.1, первичный ключ таблицы Departments создается на основе поля DepartmentName. Dim pk(0) As DataColumn pk(0) = dtDepartments.Columns("DepartmentName") dtDepartments.PrimaryKey = pk НА ЗАМЕТКУ После определения первичного ключа метод Find используется так, как показано ниже. Dim desiredRow As DataRow desiredRow = dtDepartments.Rows.Find("sales") Здесь переменной desiredRow присваивается объект DataRow с указанным значением первичного ключа или значение Nothing, если такая запись не будет найдена. Если первичный ключ таблицы основан на нескольких полях, то соответствующие значения первичного ключа передаются в виде элементов массива (типа Object) методу Find. ' Указание первичного ключа. Dim pk(0) As DataColumn pk(0) = dtEmployees.Columns("FirstName") pk(1) = dtEmployees.Columns("LastName") dtEmployees.PrimaryKey = pk ' Попытка поиска нужной записи. Dim desiredRow As DataRow Dim desiredValues (1) As Object desiredValues(0) = "Sam" desiredValues(1) = "Johnson" desiredRow = dtEmployees.Rows.Find(desiredValues) Метод Select объекта DataTable возвращает массив объектов DataRow. Возвращаемые строки могут соответствовать критерию фильтрования, порядку сортировки и/или спецификации состояния (объект DataViewRowState пространства имен System.Data). Приведенный ниже код возвращает и отображает имена всех сотрудников с фамилией Johnson. Dim selectedRows () As DataRow selectedRows = dtEmployees.Select("LastName = 'Johnson'") Dim i As Integer For i = 0 to selectedRows.GetUpperBound(0) MessageBox.Show(selectedRows(i)("FirstName")) Next Для возврата записей, отсортированных в порядке убывания, можно отредактировать строку с методом Select так, как показано ниже. selectedRows = dtEmployees. Select ("LastName = 'Johnson'", "FirstName DESC") Наконец, указание состояния записи в качестве аргумента метода Select позволяет извлекать записи с определенной версией данных непосредственно в процессе их редактирования. Например, для извлечения всех исходных значений записей даже после их редактирования (но еще до вызова метода AcceptChanges) следует указать значение OriginalRows перечисления DataViewRowState, как показано ниже. selectedRows = dtEmployees.Select(Nothing, Nothing, DataViewRowState.OriginalRows) Для отбора вновь добавленных записей с фамилией Johnson следует указать значение Added перечисления DataViewRowState, как показано ниже. selectedRows = dtEmployees. Select ("LastName = 'Johnson'", Nothing, DataViewRowState.Added) А если нужно отобрать вновь добавленные записи с фамилией Johnson и отсортировать их по имени, то в таком случае следует использовать приведенный ниже код. selectedRows = dtEmployees.Select("LastName = 'Johnson'", "FirstName DESC", DataViewRowState.Added) В табл. 5.3 приведены все возможные варианты состояния записи, которые могут быть представлены членами перечисления DataViewRowState. Упомянутые здесь изменения связаны с последней загрузкой данных или вызовом метода АcсеptChanges. Таблица 5.3. Члены перечисления DataViewRowState
Отношения между таблицамиПоскольку объект DataSet может содержать несколько таблиц, то вполне естественно, что между ними могут существовать какие-то отношения (по крайней мере, если речь идет о реляционных базах данных). В модели ADO.NET для этого предусмотрен объект DataRelation. Объект DataRelation устанавливает соответствие между полями в двух таблицах, которые имеют родительско-дочерние отношения или связаны первичным и внешним ключами. Классический пример такого отношения существует между таблицами с данными о клиентах и с данными о заказах, где одна запись клиента может быть связана с несколькими записями его заказов. Запись клиента является родительской, а записи заказов — дочерними. Продолжим обсуждение этой темы на примере родительской таблицы Department и дочерней таблицы Employees, которые находятся в одном объекте DataSet. Объект DataRelation выполняет две разные функции. • Позволяет переходить от одной связанной таблицы к другой и обратно, т.е. при работе с родительской записью можно получить доступ к ее дочерним записям, а при работе с дочерней записью — доступ к ее родительской записи. • Позволяет задавать и поддерживать ссылочную целостность, например каскадные обновления данных в связанных таблицах при выполнении каких-либо изменений в любой из связанных таблиц. Продолжим работу с упомянутым ранее проектом DataSetCode. 1. Создайте новую кнопку непосредственно под кнопкой Create DataSet, перетаскивая ее из панели элементов управления. 2. В окне свойств Properties укажите значение btnCreateRelations для свойства (Name) и значение Create Relations для свойства Text. 3. Вставьте код, показанный в листинге 5.6. Листинг 5.6. Код создания и отображения отношений между таблицамиPrivate Sub btnCreateRelations_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCreateRelations.Click Dim As DataRelation CreateDataSet() ' Создание отношения между таблицами Departments и Employees. rel = dsEmployeeInfо.Relations.Add(_ "relDepartmentEmployees", _ dsEmployeeInfo.Tables("Departments").Columns("ID"), _ dsEmployeeInfo.Tables("Employees").Columns("DepartmentID")) DisplayRelations(dsEmployeeInfo) End Sub Private Sub DisplayRelations(ByVal ds As DataSet) Dim rel As DataRelation ' Вывод имен полей созданного отношения. Me.lstOutput.Items.Add("") Me.lstOutput.Items.Add("DISPLAY RELATIONS") For Each rel In ds.Relations ' Вывод имени отношения. Me.lstOutput.Items.Add("NAME: " & rel.RelationName) ' Вывод имени родительской таблицы и ее поля, ' которое входит в созданное отношение. Me.IstOutput.Items.Add("PARENT: " & _ rel.ParentTable.ToString & " – " & _ rel.ParentColumns(0).ColumnName) ' Вывод имени дочерней таблицы и ее поля, ' которое входит в созданное отношение. Me.lstOutput.Items.Add("CHILD: " & _ rel.ChildTable.ToString & " – " & _ rel.ChildColumns(0).ColumnName) Next Me.lstOutput.Items.Add("") End Sub Сначала нужно создать объект DataRelation. Каждый объект DataSet содержит коллекцию отношений, которая доступна как свойство этого объекта Relations. Это свойство имеет тип DataRelationCollection и поддерживает несколько перегруженных версий метода Add. Версия, использованная в листинге 5.6, принимает три аргумента: имя отношения, ссылку на объект DataColumn в родительской таблице, а также ссылку на объект DataColumn в дочерней таблице. Если отношение между таблицами охватывает более одного поля, то следует использовать другую версию метода Add с аргументами-массивами объектов DataColumn. Подпрограмма DisplayRelations циклически обходит все отношения свойства Relations объекта DataSet и выводит в текстовом поле имя отношения, имя родительской таблицы и ее поле, которое входит в созданное отношение, а также имя дочерней таблицы и ее поле, которое входит в созданное отношение. НА ЗАМЕТКУ После компоновки проекта DataSetCode и запуска полученного приложения щелкните на кнопке Create Relations, и в текстовом поле будет выведена информация о вновь созданном отношении между таблицами Employees и Departments. Кроме коллекции Relations объекта DataSet, которая содержит все отношения, определенные между таблицами объекта DataSet, каждый объект DataTable также содержит две коллекции отношений (т.е. два свойства): ParentRelations и ChildRelations, которые содержат отношения между данным объектом DataTable и связанной с ним другой (дочерней или родительской) таблицей. После создания отношения между таблицами можно организовать доступ к связанным данным в них. Начните с создания кнопки и вставки кода, приведенного в листинге 5.7. 1. Создайте новую кнопку непосредственно под кнопкой Create Relations, перетаскивая ее из панели элементов управления. 2. В окне свойств Properties укажите значение btnChildRows для свойства (Name) и значение Child Rows для свойства Text. 3. Вставьте код, показанный в листинге 5.7. Листинг 5.7. Код отображения родительских и дочерних данных из связанных таблицPrivate Sub btnChildRows_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnChildRows.Click Dim rel As DataRelation CreateDataSet() AddData() ' Создание отношения между таблицами Departments и Employees rel = dsEmployeeInfо.Relations.Add("relDepartmentEmployees", _ dsEmployeeInfo.Tables("Departments").Columns("ID"), _ dsEmployeeInfo.Tables("Employees").Columns("DepartmentID")) DisplayChildRows(dsEmployeeInfo.Tables("Departments")) End Sub Private Sub DisplayChidRows(ByVal dt As DataTable) Dim rel As DataRelation Dim relatedRows() As DataRow Dim row As DataRow Dim col As DataColumn Dim i As Integer Dim rowData As String Me.lstOutput.Items.Add("") Me.lstOutput.Items.Add("CHILD ROWS") For Each row In dt.Rows For Each rel In dt.ChildRelations Me.lstOutput.Items.Add(_ dt.TableName & ": " & _ rel.ParentColumns(0).ColumnName & _ "= " & row(rel.ParentColumns(0).ToString)) relatedRows = row.GetChildRows(rel) ' Вывод значений записей. For i = 0 To relatedRows.GetUpperBound(0) rowData = "****" & _ rel.ChildTable.TableName & ":" For Each col In rel.ChildTable.Columns rowData = rowData & " " & _ relatedRows(i)(col.ToString) Next col Me.lstOutput.Items.Add(rowData) Next i Next rel Next row End Sub Подпрограмма btnChildRows_Click для обработки щелчков на кнопке Child Rows сначала создает объект DataSet и объекты DataTable с помощью подпрограммы CreateDataSet (код которой приведен в листинге 5.1), а затем наполняет их данными с помощью подпрограммы AddData (код которой приведен в листинге 5.2). После этого между таблицами Employees и Departments создается отношение с помощью кода, который приведен в листинге 5.6. Наконец, для вывода данных из записей в текстовом поле формы вызывается подпрограмма DisplayChildRows, которой в качестве аргумента (родительской таблицы) передается таблица Departments. Подпрограмма DisplayChildRows содержит трижды вложенный цикл для отображения всех полей из каждой связанной дочерней таблицы (в данном случае только одной) для каждой записи родительской таблицы. При этом каждая запись родительской таблицы передается циклу как аргумент, происходит обход всех отношений, определенных в свойстве ChildRelations таблицы, отображается имя таблицы, имя поля в родительской таблице, а также значение поля в текущей записи. Затем вызывается метод GetChildRows с текущим отношением в качестве аргумента и возвращается массив объектов DataRow, содержащих дочерние записи. Для каждой записи отображаются все поля с префиксами в виде четырех звездочек и имени дочерней таблицы. НА ЗАМЕТКУ РИС. 5.3. Результаты отображения родительских и дочерних записей из таблиц Employees и Departments После компоновки проекта DataSetCode и запуска полученного приложения щелкните на кнопке Child ROWS, и в текстовом поле будут выведены значения всех родительских записей из таблицы Employees для каждой родительской записи из таблицы Departments (рис. 5.3). ОграниченияОграничениями называются правила, которые вводятся для поддержания целостности данных в таблице. В модели ADO.NET применяется два типа ограничений целостности данных: UniqueConstraint и ForeignKeyConstraint. Ограничение UniqueConstraint гарантирует, что все значения в указанных полях будут уникальны в рамках всей таблицы. Ограничение ForeignKeyConstraint определяет связь на основе первичного и внешнего ключа в двух таблицах и выполняемые действия в случае добавления, удаления или изменения родительской записи (т.е. первичного ключа). При нарушении заданных ограничений генерируется исключительная ситуация. Учтите, что ограничения применяются только тогда, когда свойство EnforceConstraints объекта DataSet имеет значение True, которое используется по умолчанию. Хотя ограничения можно создавать непосредственно, довольно часто они создаются косвенно. Фактически в приведенном ранее примере уже было создано несколько ограничений. Объект UniqueConstraint автоматически создается и включается в коллекцию Constraints объекта DataTable, если для свойства Unique объекта DataColumn задано значение True. Кроме того, объекты UniqueConstraint и ForeignKeyConstraint автоматически создаются при создании отношения между двумя таблицами. В таком случае объект UniqueConstraint создается для связанных полей в родительской таблице, а объект ForeignKeyConstraint — для связанных полей в дочерней таблице. НА ЗАМЕТКУ Для создания и отображения ограничений таблиц используемого объекта DataSet в данном примере создадим еще одну кнопку и вставим код, приведенный в листинге 5.8. 1. Создайте новую кнопку непосредственно под кнопкой Child ROWS, перетаскивая ее из панели элементов управления. 2. В окне свойств Properties укажите значение btnConstraints для свойства (Name) и значение Constraints для свойства Text. 3. Вставьте код, показанный в листинге 5.8. Листинг 5.8. Код отображения ограниченийPrivate Sub btnConstraints_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles btnConstraints.Click Dim dt As DataTable Dim rel As DataRelation CreateDataSet() ' Создание отношения между таблицами Departments и Employees. rel = dsEmployeeInfo.Relations.Add(_ "relDepartmentEmployees", _ dsEmployeeInfo.Tables("Departments").Columns("ID"), _ dsEmployeeInfo.Tables("Employees").Columns("DepartmentID")) For Each dt In dsEmployeeInfo.Tables DisplayConstraints(dt) Next dt End Sub Private Sub DisplayConstraints(ByVal dt As DataTable) Dim i As Integer Dim cs As Constraint Dim uCS As UniqueConstraint Dim fkCS As ForeignKeyConstraint Dim columns() As DataColumn Me.lstOutput.Items.Add("") Me.lstOutput.Items.Add( _ "CONSTRAINTS FOR TABLE: " & dt.TableName) Me.lstOutput.Items.Add( _ "====================================") For Each cs In dt.Constraints Me.lstOutput.Items.Add( _ "Constraint Name: " & cs.ConstraintName) Me.lstOutput.Items.Add( _ "Type: " & cs.GetType().ToString()) If TypeOf cs Is UniqueConstraint Then uCS = CType(cs, UniqueConstraint) ' Обработка полей в виде массива columns = uCS.Columns ' Вывод имен полей. For i = 0 То columns.Length – 1 Me.lstOutput.Items.Add( _ "Column Name: " & columns(i).ColumnName) Next i ElseIf TypeOf cs Is ForeignKeyConstraint Then fkCS = CType(cs, ForeignKeyConstraint) ' Обработка дочерних полей и вывод их имён columns = fkCS.Columns For i = 0 To columns.Length – 1 Me.lstOutput.Items.Add( _ "Column Name: " & columns(i).ColumnName) Next i ' Вывод имени связанной родительской таблицы. Me.lstOutput.Items.Add( _ "Related Table Name: " & _ fkCS.RelatedTable.TableName) ' Обработка связанных родительских полей и вывод их имен columns = fkCS.RelatedColumns For i = 0 То columns.Length – 1 Me.lstOutput.Items.Add( _ "Related Column Name: " & _ columns(i).ColumnName) Next i End If Me.lstOutput.Items.Add("====================================") Next cs End Sub Подпрограмма btnConstraints_Click обрабатывает щелчки на кнопке Constraints: создает объекты DataSet, DataTable и DataRelation (с помощью кода из прежних листингов), а затем вызывает подпрограмму DisplayConstraints для отображения информации о созданных ограничениях. Подпрограмма общего типа DisplayConstraints принимает в качестве параметра объект DataTable и отображает информацию об ограничениях указанной таблицы. Для этого выполняется циклический обход всех членов свойства-коллекции Constraints указанной таблицы. Каждое найденное ограничение проверяется, т.е. выясняется, имеет ли оно тип UniqueConstraint или ForeignKeyConstraint. Оба они являются производными классами от класса Constraint, поэтому могут сосуществовать в рамках одной типизированной коллекции. Однако эти объекты все же обладают разным набором свойств, поэтому разработчику потребуется идентифицировать тип ограничения в коллекции и преобразовать его к соответствующему уточненному типу. Для ограничения UniqueConstraint отображаются имена всех (одного или нескольких) полей, определенных в данном ограничении. А для ограничения ForeignKeyConstraint отображается имя связанной родительской таблицы вместе с именами всех (одного или нескольких) связанных полей в этой таблице. РИС. 5.4. Результаты отображения информации о созданных ограничениях для таблиц Employees и Departments После компоновки проекта DataSetCode и запуска полученного приложения щелкните на кнопке Constraints, и в текстовом поле будет выведена информация о созданных ограничениях (рис. 5.4). Учтите, что все три показанных ограничения (одно для таблицы Employees и два для таблицы Departments) созданы автоматически, так как во время создания отношения между таблицами для свойства Unique объекта DataColumn задано значение True. Объект ForeignKeyConstraint имеет три свойства-правила, которые управляют действиями, предпринимаемыми при редактировании данных в связанных таблицах. Например, свойства UpdateRule и DeleteRule определяют действия, выполняемые при обновлении и удалении записей в родительской таблице. Эти свойства могут принимать одно из значений перечисления Rule, члены которого описаны в табл. 5.4. Таблица 5.4. Члены перечисления Rule
Еще одно свойство AcceptRejectRule может принимать значения Cascade (или None) при вызове метода AcceptChanges (или RejectChanges) для связанных дочерних записей. По умолчанию для него используется значение Cascade, которое указывает на автоматический вызов методов AcceptChanges или RejectChanges для дочерних записей при вызове этих методов для связанной с ними родительской записи. Если свойство AcceptRejectRule имеет значение None, то вызов одного из этих двух методов для родительской записи никак не повлияет на связанные с ней дочерние записи. Применение объекта DataSetПанель элементов управления Data среды Visual Studio .NET содержит компонент DataSet, который позволяет задавать значения свойств для набора данных с помощью окна свойств Properties вместо создания специального кода. Этот способ работы с компонентом DataSet аналогичен способам работы с компонентами Connection и Command, которые описываются в главе 4, "Модель ADO.NET: провайдеры данных". Задайте конфигурацию объекта DataSet и связанных с ними объектов с теми же определениями, которые используются в приведенных ранее фрагментах кода. Для этого выполните перечисленные ниже действия. 1. Создайте другую форму frmDataSetComponent в проекте DataSetCode. 2. В окне свойств Properties формы укажите значение DataSet для свойства Text и значение frmDataSetComponent для свойства (Name). 3. Увеличьте размер формы frmDataSetComponent. 4. Создайте в форме поле со списком, перетаскивая его из панели элементов управления. 5. В окне свойств Properties поля со списком укажите значение lstOutput для свойства (Name). 6. Увеличьте размер поля со списком lstOutput так, чтобы оно покрывало до 80% площади формы. 7. Из панели элементов управления Data перетащите в форму компонент DataSet. В появившемся на экране диалоговом окне выберите переключатель Untyped dataset (Нетипизированный набор данных) и щелкните на кнопке OK. Этот компонент невидим во время выполнения приложения, поэтому в режиме создания приложения он будет находиться под формой. 8. В окне свойств Properties этого компонента укажите значение dsEmployeeInfо для свойства (Name). 9. В окне свойств Properties этого компонента выберите свойство Tables и щелкните на кнопке с многоточием в правой части этого свойства для отображения диалогового окна Tables Collection Editor (Редактор коллекции таблиц). 10. Щелкните на кнопке Add для отображения свойств первой таблицы создаваемого набора данных. 11. В панели свойств Table1 Properties укажите значение Employees для свойства TableName, как показано на рис. 5.5. Рис. 5.5. Диалоговое окно Tables Collection Editor после указания таблицы Employees 12. В панели свойств Employees Properties выберите свойство Columns и щелкните на кнопке с многоточием в правой части этого свойства для отображения диалогового окна Columns Collection Editor (Редактор коллекции полей). 13. Щелкните на кнопке Add для отображения свойств первого поля таблицы Employees. 14. В панели свойств Column1 Properties укажите значение FirstName для свойства ColumnName первого поля. 15. Щелкните на кнопке Add для отображения свойств второго поля таблицы Employees. 16. В панели свойств Column1 Properties укажите значение LastName для свойства ColumnName второго поля. 17. Щелкните на кнопке Add для отображения свойств третьего поля таблицы Employees. 18. В панели свойств Column1 Properties укажите значение Department ID для свойства ColumnName и значение System.Int32 для свойства DataType третьего поля. После выполнения этих действий диалоговое окно Columns Collection Editor будет выглядеть так, как показано на рис. 5.6. Рис. 5.6. Диалоговое окно Columns Collection Editor со свойствами полей таблицы Employees 19. Щелкните на кнопке Close в диалоговом окне Columns Collection Editor, чтобы вернуться в диалоговое окно Tables Collection Editor для включения в набор данных dsEmployeeInfо еще одной таблицы Departments. 20. Щелкните на кнопке Add для отображения свойств второй таблицы набора данных dsEmployeeInfо. 21. В панели свойств Table1 Properties укажите значение Departments для свойства TableName второй таблицы. 22. Укажите значение 5 для свойства MinimumCapacity второй таблицы. 23. В панели свойств Departments Properties укажите свойство Columns и щелкните на кнопке с многоточием в правой части этого свойства для отображения диалогового окна Columns Collection Editor (Редактор коллекции полей). 24. Щелкните на кнопке Add для отображения свойств первого поля таблицы Departments. 25. Укажите значение ID для свойства СolumnName и значение System.Int32 для свойства DataType первого поля. 26. В панели свойств ID Properties укажите значение True для свойства Readonly, значение True для свойства Unique и значение True для свойства AutoIncrement первого поля. 27. Щелкните на кнопке Add для отображения свойств второго поля таблицы Departments. 28. В панели свойств Column1 Properties укажите значение DepartmentName для свойства ColumnName. 29. В панели свойств DepartmentName Properties укажите значение True для свой ства Unique и значение False для свойства AllowDBNull первого поля. 30. Щелкните на кнопке Close в диалоговом окне Columns Collection Editor для возвращения в диалоговое окно Tables Collection Editor, а затем щелкните еще раз на кнопке Close для закрытия диалогового окна Tables Collection Editor. Итак, вы создали набор данных dsEmployeesInfo с таблицами Employees и Departments, указывая для свойств компонентов те же значения, которые использовались в коде из листинга 5.1. Продолжим работу с этими компонентами и определим отношения между таблицами набора данных dsEmployeesInfo. 1. В окне свойств Properties компонента dsEmployeesInfo выберите свойство Relations, а затем щелкните на кнопке с изображением многоточия, чтобы отобразить на экране диалоговое окно Relations Collection Editor (Редактор коллекции отношений). 2. Щелкните на кнопке Add для отображения в диалоговом окне Relation свойств первого отношения между таблицами набора данных dsEmployeesInfo. 3. В текстовом поле Name диалогового окна Relation укажите значение relDepartmentEmployees свойства Name. 4. В текстовом поле ParentTable диалогового окна Relation выберите таблицу Departments свойства ParentTable. 5. В текстовом поле ChildTable диалогового окна Relation выберите таблицу Employees свойства ChildTable. 6. В поле со списком столбца Key Columns раздела Columns диалогового окна Relation выберите поле ID, в результате этого значение ID будет присвоено свойству ParentColumns объекта relDepartmentEmployees. 7. Аналогично в поле со списком столбца Foreign Key Columns раздела Columns диалогового окна Relation выберите поле DepartmentID, в результате значение DepartmentID будет присвоено свойству ChildColumns объекта relDepartmentEmployees. 8. Воспользуйтесь предлагаемыми по умолчанию значениями в списках Update rule (Правило обновления), Delete rule (Правило удаления) и Accept/Reject rule (Правило подтверждения/отказа), которые соответствуют свойствам UpdateRule, DeleteRule и AcceptRejectRule. 9. Щелкните на кнопке OK для закрытия диалогового окна Relation, а затем на кнопке Close для закрытия диалогового окна Relations Collection Editor. Теперь остается указать значение свойства PrimaryKey для каждой таблицы. 1. В окне свойств Properties компонента dsEmployeesInfo выберите свойство Tables, а затем щелкните на кнопке с изображением многоточия, чтобы отобразить на экране диалоговое окно Tables Collection Editor. 2. В списке членов набора данных Members выберите таблицу Employees. 3. В панели свойств Employees Properties выберите свойство PrimaryKey и щелкните на кнопке с изображением стрелки в правой части этого свойства для развертывания списка полей. 4. Установите флажки для поля (или нескольких полей) первичного ключа из списка доступных полей. Если первичный ключ охватывает несколько полей, то выберите их в правильном порядке. В данном примере выберите сначала поле FirstName, а затем LastName, как показано на рис. 5.7. РИС. 5.7. Выбор нескольких полей для определения первичного ключа 5. Нажмите клавишу <Enter> для утверждения созданного первичного ключа. 6. В списке членов набора данных Members выберите таблицу Departments. 7. В панели свойств Departments Properties выберите свойство PrimaryKey и щелкните на кнопке с изображением стрелки в правой части этого свойства для развертывания списка полей. 8. Установите флажок для поля DepartmentName первичного ключа из списка доступных полей. 9. Щелкните на кнопке Close для закрытия диалогового окна Tables Collection Editor. Чтобы убедиться в идентичности результатов, полученных в режиме создания компонентов, по сравнению с созданным ранее кодом скопируйте и вставьте некоторые подпрограммы из формы frmDataSets в форму frmDataSetComponent, а затем запустите полученное приложение, выполнив перечисленные ниже действия. 1. Выберите и скопируйте подпрограмму AddData из формы frmDataSets в форму frmDataSetComponent. 2. Повторите п. 1 для подпрограмм DisplayDataSet и DisplayChildRows. 3. Включите следующий код в обработчик события загрузки формы frmDataSetComponent_Load в форме frmDataSetComponent: Private Sub frmDataSetComponent_Load(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load AddData() DisplayDataSet() DisplayChildRows(dsEmployeelnfo.Tables("Departments")) End Sub 4. Щелкните правой кнопкой мыши на проекте DataSetCode в окне Solution Explorer и выберите в контекстном меню команду Properties, чтобы открыть диалоговое окно DataSetCode Property Pages. 5. Выберите раздел General в папке Common Properties в правой части диалогового окна DataSetCode Property Pages, а затем выберите форму frmDataSetComponent в поле Startup object. После запуска созданного приложения в поле со списком формы frmDataSetComponent будут отображены все данные набора dsEmployeeInfo и дочерние записи из таблицы Departments (рис. 5.8). НА ЗАМЕТКУ РИС. 5.8. Отображение всех данных набора dsEmployeeInfo и дочерних записей с помощью параметров объекта DataSet, заданных, в режиме создания компонентов РезюмеВ этой главе подробно описываются объекты и концепции, которые (вместе с материалом из главы 4, "Модель ADO.NET: провайдеры данных") являются ключевыми для методов создания приложений баз данных на основе ADO.NET и Visual Basic .NET. Здесь показано, как объект DataSet и связанные с ним объекты DataTable, DataRelation, DataRow и DataColumn предоставляют разработчикам гибкую и, мощную модель программирования для обработки данных при отключении от физического источника данных. В главе 6, "ADO.NET: объект DataAdapter", демонстрируются способы использования объекта DataAdapter для наполнения данными объекта DataSet и автоматического обновления источника данных при изменении данных в объекте DataSet. Вопросы и ответыНасколько я понял, источник данных можно использовать непосредственно (с помощью команд управления данными) или косвенно (в неподключенном стоянии). В каких случаях предпочтительнее использовать каждый из этих способов? Метод на основе объекта DataSet (в неподключенном состоянии) по сравнению с непосредственным применением команд управления данными обладает несколькими преимуществами. Он предлагает более простой и единообразный способ перемещения данных между разными уровнями и местоположениями в распределенной базе данных, а также между разными приложениями баз данных благодаря встроенной поддержке языка XML. В нем предусмотрен механизм кэширования данных, что позволяет сортировать, фильтровать и искать данные без доступа к источнику данных. Наконец, он позволяет извлекать данные из нескольких таблиц или даже нескольких разных источников данных, а также манипулировать ими индивидуально или совместно на основе заданных между ними отношений. Прямой способ доступа к источнику данных с помощью объекта Command также обладает определенными преимуществами. Некоторые операции, например изменение структуры базы данных, можно выполнить только с помощью прямого доступа. При прямом доступе даже стандартные команды SQL или хранимые процедуры могут быть выполнены быстрее и эффективнее, что позволяет добиться более высокой производительности и масштабируемости. Кроме того, этот способ позволяет сократить объем оперативной памяти для объекта DataSet, особенно при отсутствии насущной необходимости в кэшировании данных, например при создании Web-страницы или заполнении данными поля со списком. Итак, когда же предпочтительнее использовать прямой доступ к базе данных вместо объекта DataSet? Прежде всего в тех случаях, когда операция может быть выполнена только с помощью объекта Command. Это относится к вызовам хранимых процедур, которые выполняют манипуляции с данными и возвращают только одно значение и/или значения параметров, а также к DDL-командам изменения структуры базы данных. Кроме того, не рекомендуется использовать объект DataSet, если данные используются только для чтения, используются недолго, а потому не оправданны их загрузка и хранение в оперативной памяти, либо используются сервером и их не нужно передавать на другие уровни приложения или компьютеры. В большинстве других случаев предпочтительнее обращаться к объекту DataSet. |
|
||||||||||||||||||||||||||||||||||||||||||||||||||
Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх |
||||||||||||||||||||||||||||||||||||||||||||||||||||
|