ADO.NET. Управление базами данных. Связь по схеме OLE DB провайдера. Коррекция стилей DataGridView, страница 11

DELETE FROM Studs WHERE StudID = ?

UPDATE Studs SET Name = ?, Phone = ?, Addr = ? WHERE StudID = ?

Как бороться с Concurrency Exception

Самым простым способом борьбы с Concurrency Exception является чтение всех данных из источника после каждого обновления. При этом, во-первых, видно, как восприняты исправления, во-вторых, исчезает исключение (дело в том, что после чтения данных строка восстанавливает истинный первичный ключ). Однако, такое решение является дорогим удовольствием, когда либо сама таблица велика, либо велика конкуренция в ее использовании. И главное, такой подход не соответствует парадигме ADO.NET — работа в отсоединенном режиме.

Другим способом является попытка поймать и обработать исключение. В ветви обработки catch мы можем, например, сообщить о том, что изменение не прошло и все-таки прочесть данные заново, с тем, чтобы следующая попытка уже удалась. Да, здесь вновь используется дорогая операция чтения всей таблицы (Fill), но она выполняется реже (только в случаях, когда возникает исключение). Для практики полезно реализовать оба эти способа (если вы располагаете запасом времени). В качестве подсказки сообщим, что ловить (catch) следует объект класса DBConcurrencyException.

Еще одним способом устранения недостатка информации (первичного ключа), рекомендуемым в таких случаях, является настройка адаптера:

  da.MissingSchemaAction = MissingSchemaAction.AddWithKey; // Генерация уникальных  значений первичного ключа

Она определяет действие, которое будет выполнено адаптером в случае, когда входные данные не удовлетворяют схеме DataSet. Выбранный элемент перечисления — AddWithKey определяет действие, которое добавляет необходимые колонки и генерирует значения первичного ключа так, чтобы они соответствовали схеме.

В нашем случае набор колонок полностью соответствует схеме, поэтому адаптер будет лишь автоматически вставлять значения первичного ключа так, чтобы обеспечить его уникальность. Но то, что вставляет адаптер может не соответствовать тому, что делает драйвер Access. Более подробно, но достаточно туманно это действие описано в документации MSDN (см. разделы FillSchema и MissingSchemaAction Enumeration).

·  Вставьте настройку свойства MissingSchemaAction и не меняйте ее в процессе доводки приложения.

·  Сравните поведение DataGridView при переходе в новую строку с тем, что было до установки свойства.

·  Заметьте, что первичный ключ автоматически генерируется и вставляется, а предыдущий сценарий не выбрасывает исключение, если драйвер Access не ушел в своей нумерации StudID от последнего индекса.

Однако в более сложном сценарии (вставьте три строки, нажмите Save, удалите первую и третью строки и вновь нажмите Save) этот способ не помогает уберечься от Concurrency violation. Поработайте с данными более интенсивно и убедитесь, что исключение проявит себя. Это произойдет, когда значение ключа, сгенерированное адаптером, не совпадет со значением сгенерированным драйвером Access.

Коррекция идентичности строк

В борьбе с DBConcurrencyException поможет способ, основанный на выявлении действительного значения первичного ключа, сгенерированного Access. Узнать ключ можно с помощью отдельного запроса к базе данных. Делать это удобно в обработчике события RowUpdated. Для того чтобы понять, почему именно в нем, приведем хронологию событий при выполнении метода Update (материал взят из MSDN — может измениться):

·  Значения обновляемой строки таблицы (объекта класса DataRow) помещаются в параметры нужной команды (одной из 3-х OleDbCommand команд обновления Insert, Delete, Update, генерируемой построителем).

·  Возбуждается событие OnRowUpdating.

·  Выполняется команда. Если она установлена на возврат первой записи (FirstReturnedRecord), то результат помещается в объект DataRow. Если команда имеет выходные параметры, то они памещаются в DataRow.

·  Возбуждается событие OnRowUpdated.

·  Вызывается метод AcceptChanges, который закрепляет изменения в базе.