Пошаговое руководство. Обработка исключения параллельности
Исключения совместного доступа (DBConcurrencyException) вызываются, когда два пользователя пытаются одновременно изменить одни и те же данные в базе данных. В этом пошаговом руководстве создается приложение Windows, описывающее перехват DBConcurrencyException, поиск строки, вызвавшей ошибку, и одну стратегия, которую можно использовать для его обработки.
В данном пошаговом руководстве необходимо выполнить следующий процесс:
Создать новый проект Приложение Windows.
Создать новый набор данных на основе таблицы Customers базы данных "Борей".
Создать форму с DataGridView для отображения данных.
Заполнить набор данных данными из таблицы Customers базы данных "Борей".
После заполнения набора данных необходимо использовать Визуальные инструменты для баз данных в Visual Studio для непосредственного доступа к таблице данных Customers и изменения записи.
Затем в форме изменить значение в той же записи на другое, обновить набор данных и попытаться записать изменения в базу данных, что приведет к ошибке совместного доступа.
Перехватить ошибку, а затем отобразить различные версии записей, предоставляя пользователю возможность продолжить обновление базы данных, либо отменить его.
Обязательные компоненты
Для выполнения этого пошагового руководства потребуется следующее.
- Доступ к образцу базы данных "Борей" с разрешением на выполнение обновления. Дополнительные сведения см. в разделе Практическое руководство. Установка образцов баз данных.
Примечание
Отображаемые диалоговые окна и команды меню могут отличаться от описанных в справке в зависимости от текущих настроек или выпуска.Чтобы изменить параметры, выберите в меню Сервис пункт Импорт и экспорт параметров.Дополнительные сведения см. в разделе Работа с параметрами.
Создание нового проекта
Пошаговое руководство начинается с создания нового приложения Windows.
Чтобы создать проект приложения Windows
Из меню Файл создайте новый проект.
Выберите язык программирования в области Типы проектов.
Выберите Приложение Windows в области Шаблоны.
Назовите проект ConcurrencyWalkthrough и нажмите кнопку OK.
Visual Studio добавит проект в Обозреватель решений и откроет новую форму в конструкторе.
Создание набора данных в базе данных "Борей"
В этом разделе создается набор данных с именем NorthwindDataSet.
Чтобы создать NorthwindDataSet
В меню Данные выберите Добавить новый источник данных.
Откроется окно мастер настройки источника данных.
На странице Выбор типа источника данных выберите База данных.
Выберите подключение к образцу базы данных "Борей" из списка доступных подключений или нажмите кнопку Создать подключение, если подключение недоступно в списке подключений.
Примечание
Если вы подключаетесь к файлу локальной базы данных, выберите Нет в ответ на вопрос о том, необходимо ли добавить файл в проект.
Нажмите Далее на странице Сохранение подключения в файле конфигурации приложения.
Разверните узел Таблицы и выберите таблицу Customers. По умолчанию набор данных должен называться NorthwindDataSet.
Нажмите кнопку Готово, чтобы добавить набор данных в проект.
Создание элемента управления с привязкой к данным DataGridView
В этом разделе создается DataGridView путем перетаскивания элемента Customers из окна Источники данных на форму Windows Form.
Чтобы создать элемент управления DataGridView, привязанный к таблице Customers
Выберите Показать Источники данных в меню Данные для открытия окна Источники данных.
В окне Источники данных разверните узел NorthwindDataSet и выберите таблицу Customers.
Щелкните стрелку вниз на узле таблицы и выберите DataGridView из раскрывающегося списка.
Перетащите таблицу в пустую область формы.
Элемент управления DataGridView с именем CustomersDataGridView и BindingNavigator с именем CustomersBindingNavigator добавляются на форму, связанную с BindingSource, который, в свою очередь, связан с таблицей в Customers NorthwindDataSet.
Контрольная точка
Теперь можно проверить форму, чтобы убедиться, что она ведет себя, как ожидалось до сих пор.
Чтобы проверить форму, выполните следующие действия:
Нажмите клавишу F5 для запуска приложения.
Появится форма с элементом управления DataGridView, заполненным данными из таблицы Customers.
В меню Отладка выберите команду Остановить отладку.
Обработка ошибок совместного доступа
Способ обработки ошибок зависит от бизнес-правил, которым подчиняется приложение. В данном пошаговом руководстве после возникновения ошибки совместного доступа в качестве иллюстрации будет использоваться следующая стратегия ее обработки:
Приложение предоставляет пользователю три версии записи:
Текущая запись в базе данных.
Исходная запись, загруженная в набор данных.
Предлагаемые изменения в наборе данных.
Далее пользователь может либо перезаписать базу данных предложенными изменениями, либо отменить обновление и обновить набор данных новыми значениями из базы данных.
Чтобы включить обработку ошибок совместного доступа
Создайте пользовательский обработчик ошибок.
Отобразите пользователю варианты выбора.
Обработайте ответ пользователя.
Отправьте обновления или сбросьте данные в наборе данных.
Добавление кода обработки исключений совместного доступа
Когда при попытке выполнить обновление возникает исключение, обычно требуется сделать что-нибудь со сведениями, предоставляемыми возникшим исключением.
В этом разделе добавляется код, который будет пытаться обновить базу данных и обработать все DBConcurrencyException, которые могут возникать, а также любые другие исключения.
Примечание
Методы CreateMessage и ProcessDialogResults будут добавлены позже в этом пошаговом руководстве.
Чтобы добавить обработку ошибок совместного доступа
Добавьте следующий код под методом Form1_Load:
Private Sub UpdateDatabase() Try Me.CustomersTableAdapter.Update(Me.NorthwindDataSet.Customers) MsgBox("Update successful") Catch dbcx As Data.DBConcurrencyException Dim response As Windows.Forms.DialogResult response = MessageBox.Show(CreateMessage(CType(dbcx.Row, NorthwindDataSet.CustomersRow)), "Concurrency Exception", MessageBoxButtons.YesNo) ProcessDialogResult(response) Catch ex As Exception MsgBox("An error was thrown while attempting to update the database.") End Try End Sub
private void UpdateDatabase() { try { this.customersTableAdapter.Update(this.northwindDataSet.Customers); MessageBox.Show("Update successful"); } catch (DBConcurrencyException dbcx) { DialogResult response = MessageBox.Show(CreateMessage((NorthwindDataSet.CustomersRow) (dbcx.Row)), "Concurrency Exception", MessageBoxButtons.YesNo); ProcessDialogResult(response); } catch (Exception ex) { MessageBox.Show("An error was thrown while attempting to update the database."); } }
Замените метод CustomersBindingNavigatorSaveItem_Click для вызова метода UpdateDatabase, чтобы он выглядел следующим образом:
Private Sub CustomersBindingNavigatorSaveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CustomersBindingNavigatorSaveItem.Click UpdateDatabase() End Sub
private void customersBindingNavigatorSaveItem_Click(object sender, EventArgs e) { UpdateDatabase(); }
Отображение вариантов выбора
Только что написанный код вызывает процедуру CreateMessage для отображения пользователю сведений об ошибке. В рамках настоящего пошагового руководства окно сообщений используется для отображения пользователю различных версий записи и предоставления выбора: перезаписать запись с новыми изменениями или отменить изменения. Когда пользователь выбирает параметр (нажимает кнопку) в окне сообщения, ответ передается методу ProcessDialogResult.
Чтобы создать сообщение для отображения пользователю
Создайте сообщение, добавив следующий код в Редактор кода. Введите этот код под методом UpdateDatabase.
Private Function CreateMessage(ByVal cr As NorthwindDataSet.CustomersRow) As String Return "Database: " & GetRowData(GetCurrentRowInDB(cr), Data.DataRowVersion.Default) & vbCrLf & "Original: " & GetRowData(cr, Data.DataRowVersion.Original) & vbCrLf & "Proposed: " & GetRowData(cr, Data.DataRowVersion.Current) & vbCrLf & "Do you still want to update the database with the proposed value?" End Function '-------------------------------------------------------------------------- ' This method loads a temporary table with current records from the database ' and returns the current values from the row that caused the exception. '-------------------------------------------------------------------------- Private TempCustomersDataTable As New NorthwindDataSet.CustomersDataTable Private Function GetCurrentRowInDB( ByVal RowWithError As NorthwindDataSet.CustomersRow ) As NorthwindDataSet.CustomersRow Me.CustomersTableAdapter.Fill(TempCustomersDataTable) Dim currentRowInDb As NorthwindDataSet.CustomersRow = TempCustomersDataTable.FindByCustomerID(RowWithError.CustomerID) Return currentRowInDb End Function '-------------------------------------------------------------------------- ' This method takes a CustomersRow and RowVersion ' and returns a string of column values to display to the user. '-------------------------------------------------------------------------- Private Function GetRowData(ByVal custRow As NorthwindDataSet.CustomersRow, ByVal RowVersion As Data.DataRowVersion) As String Dim rowData As String = "" For i As Integer = 0 To custRow.ItemArray.Length - 1 rowData &= custRow.Item(i, RowVersion).ToString() & " " Next Return rowData End Function
private string CreateMessage(NorthwindDataSet.CustomersRow cr) { return "Database: " + GetRowData(GetCurrentRowInDB(cr), DataRowVersion.Default) + "\n" + "Original: " + GetRowData(cr, DataRowVersion.Original) + "\n" + "Proposed: " + GetRowData(cr, DataRowVersion.Current) + "\n" + "Do you still want to update the database with the proposed value?"; } //-------------------------------------------------------------------------- // This method loads a temporary table with current records from the database // and returns the current values from the row that caused the exception. //-------------------------------------------------------------------------- private NorthwindDataSet.CustomersDataTable tempCustomersDataTable = new NorthwindDataSet.CustomersDataTable(); private NorthwindDataSet.CustomersRow GetCurrentRowInDB(NorthwindDataSet.CustomersRow RowWithError) { this.customersTableAdapter.Fill(tempCustomersDataTable); NorthwindDataSet.CustomersRow currentRowInDb = tempCustomersDataTable.FindByCustomerID(RowWithError.CustomerID); return currentRowInDb; } //-------------------------------------------------------------------------- // This method takes a CustomersRow and RowVersion // and returns a string of column values to display to the user. //-------------------------------------------------------------------------- private string GetRowData(NorthwindDataSet.CustomersRow custRow, DataRowVersion RowVersion) { string rowData = ""; for (int i = 0; i < custRow.ItemArray.Length ; i++ ) { rowData = rowData + custRow[i, RowVersion].ToString() + " "; } return rowData; }
Обработка ответа пользователя
При обработке ответа пользователя для окна сообщений требуется код. Можно либо перезаписать текущую запись в базе данных с предложенными изменениями, либо сбросить локальные изменения и обновить таблицу записью, содержащейся в данный момент в базе данных. Если пользователь выбрал значение "Да", метод Merge вызывается с аргументом preserveChanges, равным true. В результате обновление считается успешно завершенным, поскольку исходная версия записи теперь совпадает с версией записи в базе данных.
Чтобы обработать введенные пользователем данные из окна сообщений
Добавьте следующий код под кодом, добавленным в предыдущем разделе.
' This method takes the DialogResult selected by the user and updates the database ' with the new values or cancels the update and resets the Customers table ' (in the dataset) with the values currently in the database. Private Sub ProcessDialogResult(ByVal response As Windows.Forms.DialogResult) Select Case response Case Windows.Forms.DialogResult.Yes NorthwindDataSet.Customers.Merge(TempCustomersDataTable, True) UpdateDatabase() Case Windows.Forms.DialogResult.No NorthwindDataSet.Customers.Merge(TempCustomersDataTable) MsgBox("Update cancelled") End Select End Sub
// This method takes the DialogResult selected by the user and updates the database // with the new values or cancels the update and resets the Customers table // (in the dataset) with the values currently in the database. private void ProcessDialogResult(DialogResult response) { switch (response) { case DialogResult.Yes: northwindDataSet.Merge(tempCustomersDataTable, true, MissingSchemaAction.Ignore); UpdateDatabase(); break; case DialogResult.No: northwindDataSet.Merge(tempCustomersDataTable); MessageBox.Show("Update cancelled"); break; } }
Проверка
Теперь можно проверить форму, чтобы убедиться, что она работает так, как ожидалось. Для имитации ошибки совместного доступа необходимо изменить данные в базе данных после заполнения NorthwindDataSet.
Чтобы проверить форму, выполните следующие действия:
Нажмите клавишу F5 для запуска приложения.
После отображения формы оставьте ее запущенной и переключитесь на интегрированную среду разработки Visual Studio.
В меню Вид выберите Обозреватель серверов.
В окне Обозреватель серверов разверните узел подключения, используемого приложением, и разверните узел Таблицы.
Щелкните правой кнопкой мыши на таблице Customers и выберите Показать таблицу данных.
В первой записи (ALFKI) измените ContactName на Maria Anders2.
Примечание
Перейдите на другую строку для фиксации изменений.
Переключитесь на запущенную форму ConcurrencyWalkthrough.
В первой записи на форме (ALFKI) измените ContactName на Maria Anders1.
Нажмите кнопку Сохранить.
Возникнет ошибка совместного доступа, и появится окно сообщения.
Нажатие кнопки Нет отменяет обновление и обновляет набор данных значениями, содержащимися в данный момент в базе данных, тогда как нажатие Да записывает предложенное значение в базу данных.
См. также
Основные понятия
Новые возможности разработки приложений для работы с данными
Привязка элементов управления Windows Forms к данным в Visual Studio
Привязка элементов управления к данным в Visual Studio
Другие ресурсы
Пошаговые руководства работы с данными
Подключение к данным в Visual Studio
Подготовка приложения к получению данных