Обработка баз данных на Visual Basic®.NET - Джеффри Мак-Манус
Шрифт:
Интервал:
Закладка:
Команды SQL UPDATE и DELETE, сгенерированные объектом CommandBuilder и программой-мастером Data Adapter Configuration Wizard, содержат предложение WHERE для определения конфликтов параллельного доступа. Рассмотрим более внимательно код из главы 6, "ADO.NET: объект DataAdapter", созданный с помощью программы-мастера Data Adapter Configuration Wizard. Для этого нужно открыть раздел кода с заголовком Windows Form Designer generated code (Код, сгенерированный конструктором Windows Form) в коде формы frmUpdates. Но сначала рассмотрим команду SQL UPDATE, которая приводится в листинге 7.1.
Листинг 7.1. Команда SQL UPDATE, созданная программой-мастером Data Adapter Configuration WizardUPDATE tblEmployee
SET FirstName = @FirstName,
LastName = @LastName,
DepartmentID = @DepartmentID,
Salary = @Salary
WHERE
(ID = @Original_ID) AND
(DepartmentID = @Original_DepartmentID OR
@Original_DepartmentID IS NULL AND DepartmentID IS NULL)
AND (FirstName = @Original_FirstName)
AND (LastName = @Original_LastName)
AND (Salary = @Original_Salary OR
@Original_Salary IS NULL AND Salary IS NULL)
;
SELECT FirstName, LastName, DepartmentID, Salary, ID
FROM tblEmployee WHERE (ID = @ID)
Эта команда SQL выглядит как обычная команда UPDATE, которая задает новые значения для четырех обновляемых полей в качестве параметров объекта UpdateCommand. Предложение WHERE содержит первичный ключ (ID), а также исходные значения для каждого поля и проверяет их соответствие текущим значениям записи в базе данных. Более того, эта команда SQL проверяет наличие неопределенных значений NULL в базе данных и полях таблицы tblEmployee.
Команда SELECT (заданная при конфигурировании объекта DataAdapter) располагается вслед за командой UPDATE после точки с запятой. Точка с запятой всегда используется для разделения команд в пакете команд SQL, а команда SELECT добавляется по умолчанию для возвращения обновленной записи в приложение.
Рассмотрим код указания параметров для объекта UpdateCommand, как показано в листинге 7.2.
Листинг 7.2. Код установки параметров команды, сгенерированной с помощью программы-мастера Data Adapter Configuration WizardMe.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter("@FirstName", System.Data.SqlDbType.VarChar, 50, "FirstName"))
Me.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter("@LastName", System.Data.SqlDbType.VarChar, 70, "LastName"))
Me.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter("@DepartmentID", System.Data.SqlDbType.Int, 4, "DepartmentID"))
Me.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter("@Salary", System.Data.SqlDbType.Money, 8, "Salary"))
Me.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter("@Original_ID", System.Data.SqlDbType.Int, 4, System.Data.ParameterDirection.Input, False, CType(0, Byte), CType(0, Byte), "ID", System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter("@Original_DepartmentID", System.Data.SqlDbType.Int, 4, System.Data.ParameterDirection.Input, False, CType(0, Byte), CType(0, Byte), "DepartmentID", System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter("@Original_FirstName", System.Data.SqlDbType.VarChar, 50, System.Data.ParameterDirection.Input, False, Byte), CType(0, Byte), "FirstName", System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter("@Original_LastName", System.Data.SqlDbType.VarChar, 70, System.Data.ParameterDirection.Input, False, CType(0, Byte), CType(0, Byte), "LastName", System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter(System.Data.SqlDbType.Money, 8, System.Data.ParameterDirection.Input, False, CType(0, Byte), CType(0, Byte), "Salary", System.Data.DataRowVersion.Original, Nothing))
Me.SqlUpdateCommand1.Parameters.Add(New System.Data.SqlClient.SqlParameter("@ID", System.Data.SqlDbType.Int, 4, "ID"))
Для данного объекта-команды заданы десять параметров. Первые четыре параметра являются текущими (возможно, измененными) значениями полей, которые следует обновить в базе данных. Как сообщается в главе 5, "ADO.NET: объект DataSet", каждая запись может иметь одну из четырех версий значения в каждой записи. По умолчанию используется текущее значение из считываемого поля.
Следующие пять параметров являются исходными значениями полей, которые упоминаются в предложении WHERE. Учтите, что для извлечения исходной версии значения поля (а не предлагаемой по умолчанию текущей версии) нужно явно указать исходную версию, как показано ниже.
System.Data.DataRowVersion.Original
НА ЗАМЕТКУНеобязательно использовать исходные версии значения для всех полей в предложении WHERE. Можно настроить нужным образом любые команды обновления, чтобы уведомить об обновлении только одного или двух полей записи. В таком случае достаточно обновить только отдельные поля, а не все поля сразу.
Последний параметр — текущее значение поля ID — применяется в качестве параметра команды SELECT, используемой для возвращения обновленных значений записи.
После каждой операции вставки, обновления или удаления объект DataAdapter проверяет количество строк, охваченных операцией. Если количество строк равно нулю, то генерируется исключительная ситуация DBConcurrencyException, поскольку обычно предполагается, что это результат конфликта при параллельном доступе к данным. Для ее перехвата и обработки следует включить блок Try-Catch в подпрограмму btnUpdate_Click, которая показана в листинге 7.3.
Листинг 7.3. Блок Try-Catch для обработки исключительной ситуации DBConcurrencyExceptionPrivate Sub btnUpdate_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnUpdate.Click
Try
daEmployees.Update(dsEmployeeInfo, "Employees")
Catch ec As DBConcurrencyException
' Выполнить какие-то действия.
Catch es As DBConcurrencyException
MessageBox.Show(es.Message)
End Try
End Sub
Отображения таблиц и полей
Объект DataAdapter содержит коллекцию объектов DataTableMapping. Они предназначены для отображения имен таблиц и полей в источнике данных на их имена в объекте DataSet. Конечно, сразу после конфигурирования этот способ работает в обоих направлениях, т.е. при чтении данных из источника в объект DataSet и при записи данных в источник из объекта DataSet.
В данном примере в методе Fill объекта DataAdapter указывается объект DataSet и имя таблицы в этом наборе данных.
daEmployees.Fill(dsEmployeelnfo, "Employees")
Однако на самом деле второй аргумент метода Fill содержит имя объекта — отображения имени таблицы. Объект DataAdapter ищет определенное отображение с таким же именем и, если находит, использует полученную информацию для выполнения метода Fill. А если отображение не найдено, то создается таблица с именем переданного параметра и в нее вставляются данные.
Это значит, что можно, например, добавить отображение MappingName и отобразить его на таблицу empDataSetTable с помощью кода
daEmployees.TableMappings.Add("MappingName", "empDataSetTable")
А затем можно вызвать метод Fill следующим образом:
daEmployees.Fill(dsEmployeeInfo, "MappingName")
При этом считанные данные вставляются в таблицу empDataSetTable объекта dsEmployeeInfo.
После определения отображения таблицы можно приступать к созданию отображений полей. Эта операция имеет особенно большое значение при использовании в коде приложения имен полей, которые отличаются от имен, использованных в источнике данных. При работе с отображением таблицы для вставки данных в объект DataSet объект DataAdapter ищет все имеющиеся отображения полей для заданного отображения таблицы и использует их для отображения имен полей. При этом в объекте DataSet для всех полей, у которых нет отображения, будут использованы имена полей из источника данных.
Например, тестовая баз данных pubs, которая входит в состав SQL Server, была создана много лет назад, когда существовали очень строгие ограничения для имен полей, поэтому сейчас они имеют вид малопонятных аббревиатур. Отображения полей позволяют загружать данные из базы данных pubs в объект – набор данных с более понятными именами полей. Благодаря отображению удается удовлетворить все самые строгие требования и соглашения об именах, которые могут предъявляться со стороны администратора базы данных и архитектора приложения.
Продолжим работу с примером отображения таблицы и попробуем отобразить имена полей из источника данных, которые соответственно соглашению об именах, заданному архитектором приложения, начинаются с трёхсимвольного префикса. В таком случае код отображения будет выглядеть так, как в листинге 7.4.
Листинг 7.4. Отображение таблицы и полейdaEmployees.TableMappings.Add("MappingName", "empDataSetTable")
With daEmployees.TableMappings("MappingName").ColumnMappings
.Add("ID", "empEmployeeID")
.Add("FirstName", "empFirstName")
.Add("LastName", "empLastName")
.Add("DepartmentID", "empDepartmentID")
.Add("Salary", "empSalary")
End With
daEmployees.Update(dsEmployeeInfo, "MappingName")
В главе 6, "ADO.NET: объект DataAdapter" (см. листинги 6.1 и 6.2) представлена функция Read Data, которая вставляет в объект DataSet данные из источника данных и отображает содержимое объекта DataSet в списке формы frmDataSets (см. проект DataSetCode из главы 5, "ADO.NET: объект DataSet"). Если в подпрограмме обработки щелчков на кнопке DataAdapter Fill вместо функции ReadData вызвать ее измененную версию ReadDataMapped, которая содержит код, показанный в листинге 7.5, то после запуска приложения проекта DataSetCode будет получен результат, показанный на рис. 7.1.