Тверской государственный университет
Опубликован: 13.09.2006 | Доступ: свободный | Студентов: 4956 / 315 | Оценка: 4.23 / 3.83 | Длительность: 28:12:00
Специальности: Программист, Менеджер
Лекция 6:

Объекты ADO (продолжение)

В предыдущей главе было дано общее представление об объектах ADO и подробно рассмотрены свойства, методы и события трех основных объектов - Connection, Command, Recordset. Но я хочу продолжить разговор и дать представление не только о главных героях, но и всех остальных объектах этой модели. Но прежде чем рассматривать новые объекты, давайте разберемся в таком важном вопросе, как обрабатываются события, возникающие с объектами ADO, и о том, как справляться с ошибками, возникающими в процессе работы с этими объектами. Я напомню, что событиями обладают лишь два объекта - Recordset и Connection.

Построение обработчиков событий и обработка ошибок

Давайте разберемся с деталями построения обработчиков событий объектов ADO. Прежде всего, рассмотрим, как они создаются, в какой модуль проекта их следует поместить. Хотя объекты Recordset и Connection обладают событиями, но появляются они как объекты без событий. Необходимо предпринять обычные для VBA в таких ситуациях действия, чтобы создать объекты с событиями и написать процедуры, обрабатывающие события, - обработчики событий. В первом томе и других книгах серии "Офисное программирование" я подробно рассказывал о том, как это делается. Напомню, что для решения этой задачи нужно выполнить три шага:

  1. Создать на VBA собственный класс с именем, например MyEventsADO, в котором описать вложенные объекты - Connection WithEvents и Recordset WithEvents.
  2. Создать обработчики нужных событий для данных объектов в классе MyEventsADO. Заметьте, после объявления переменных With Events в этом классе появится возможность написания обработчиков событий, следуя обычной технологии.
  3. В подходящем месте, например в уже существующем модуле TestingADO, создать экземпляры класса MyEventsADO и связать их с глобальными или локальными объектами Rst1 и Con1, задающими набор записей и соединение.

Чтобы сделать дальнейшее описание конкретным, давайте рассмотрим создание обработчика события WillChangeField объекта Recordset, которое, напомню, появляется при попытках изменить значение поля в записи набора, но перед тем, как это изменение реально произойдет. Вот как выглядит модуль класса MyEventsADO, содержащий обработчик этого события:

'Класс MyEventsADO, определяющий события ADO
Public WithEvents EvRst As ADODB.Recordset
Public WithEvents EvCon As ADODB.Connection

Private Sub EvRst_WillChangeField(ByVal cFields As Long, _
 ByVal Fields As Variant, adStatus As ADODB.EventStatusEnum, _
 ByVal pRecordset As ADODB.Recordset)
 'Печать параметров обработчика события
 MsgBox "Номер поля = " & cFields & vbCrLf & _
 "Значение поля = " & Fields(cField) & vbCrLf & _
 "Позиция записи в наборе = " & _
 pRecordset.AbsolutePosition & vbCrLf & _
 "Статус выполняемой операции = " & adStatus
 'Изменение статуса приведет к появлению ошибки
 'при попытке изменить значение поля, обработка которой
 'позволит отменить операцию.
 If IsNumeric(Fields(cField)) Then
 If Fields(cField) > 75 Then adStatus = adStatusCancel
 End If
End Sub

Первым делом я включил в обработчике события WillChangeField печать всех его параметров на входе. Обратите внимание, обработчику события передается указатель на сам объект Recordset, поэтому доступна практически любая информация, связанная с этим объектом. Я, например, использовал его, чтобы вывести на печать позицию записи в наборе, поля которой обновляются. Во второй части обработчика я изменяю значение статуса при выполнении некоторого условия на обновляемое поле. Делаю это для того чтобы реализовать возможность отмены изменения значения, когда в результате анализа, проведенного в обработчике, принимается решение о нежелательности выполнения изменений. Заметьте, для этого свойству статус, которое имело значение adStatusOk, уведомляющее о том, что изменения возможны, я присваиваю значение adStausCancel. В этом случае при выходе из обработчика возникнет ошибка в операторе, производящем изменения. Обработав соответствующим образом эту ошибку, я смогу выполнить поставленную задачу.

Давайте теперь рассмотрим процедуру, в которой происходит обновление полей записей набора, чьи действия будут вызывать появление события WillChangeField. Эта процедура изменяет цену у некоторых книг из таблицы "Книги" нашей тестовой базы данных и добавляет новую запись в эту таблицу. При всех этих изменениях начнут работать события:

Public Sub CreateEvents()
	'Объект с событиями
	Dim imEvRst As New MyEventsADO
	 'добавление и изменение записей	базы данных
	Dim recExist As Boolean
	' Связывание объекта с событиями
	Set imEvRst.EvRst = Rst1
	'Создать соединение
	CreateConnection
	'Создать команду
	'задание свойств объекта Command
	Cmd1.ActiveConnection = Con1
	Cmd1.CommandText = "Select * From [Книги]"
	Cmd1.CommandType = adCmdText
	'Открытие обновляемого объекта Recordset
	With Rst1
		.Open Source:=Cmd1, CursorType:=adOpenDynamic, _
			LockType:=adLockOptimistic
			'Изменение записей
		recExist = False
		.MoveFirst
		Do While Not .EOF
			'Обработка текущей записи
			On Error Resume Next
			If !Название = "Офисное программирование" Then recExist = True
			If ![Год издания] < 2000 And !Цена < 100 Then
				MsgBox "Старая цена =" & Str(!Цена)
				!Цена = !Цена * 2
				.Update
			End If
			.MoveNext
		Loop
		If Not recExist Then
			.AddNew
			!Автор = "Владимир Биллиг"
			!Название = "Офисное программмирование"
			![Год издания] = 2001
			![Число страниц] = 599
			!Цена = 150
			.Update
		End If
	End With
End Sub

Обратите внимание, первым делом я объявляю объект imEvRst созданного класса MyEventsADO и связываю его с глобальным объектом Rst1, после чего последний начнет реагировать на события. Сколько раз будет появляться событие WillChangeField? Очевидно, при каждом выполнении оператора !Цена = !Цена * 2, изменяющего значение соответствующего поля записи. Вот как выглядят окошки функции MsgBox, одно из которых открывается перед выполнением этого оператора, а второе в обработчике события в момент выполнения оператора:

Окна, открываемые перед изменением поля и в момент изменения

Рис. 6.1. Окна, открываемые перед изменением поля и в момент изменения

Соответствующие сообщения появляются и при создании новой записи, причем столько раз, сколько полей имеет запись.

Важную роль в этой процедуре играет и оператор On Error Resume Next. Всякий раз, когда в обработчике события изменяется значение переменной adStatus, возникнет ошибка при выполнении оператора, изменяющего значение поля и послужившего причиной возникновения события. Оператор OnError позволяет в этом случае обойти выполнение оператора и, тем самым, избежать изменения значения, что и хотелось. Взгляните, как выглядит окно, уведомляющее об ошибке, появляющееся, если в процедуре закомментировать оператор On Error:

Окно, уведомляющее о возникновении ошибки при изменении значения

Рис. 6.2. Окно, уведомляющее о возникновении ошибки при изменении значения

Хочу теперь рассмотреть пример построения обработчика события, возникающего после завершения операции. Пример хочу построить такой, чтобы при выполнении операции Провайдер столкнулся с трудностями и сформировал объекты Error, а обработчик события соответственно обрабатывал эти ошибки. Но прежде чем перейти к рассмотрению этого примера, есть смысл более подробно познакомиться с объектом Error - его свойствами и методами.

Коллекция Errors и объекты Error

Напомню, что элементы коллекции Errors появляются при выполнении очередной команды, когда Провайдер сталкивается с определенными трудностями и не может нормальным образом завершить выполнение команды. Коллекция Errors всегда связана только с одной командой, - при первой ошибке в новой команде происходит чистка старого содержимого коллекции. Свойство Errors объекта Connection возвращает эту коллекцию. Добавлять программно элементы в коллекцию невозможно, это делается автоматически, когда Провайдер обнаруживает новую ошибку.

Свойства и методы коллекции Errors

У коллекции два свойства:

  • Count - возвращает число элементов коллекции,
  • Item - свойство по умолчанию позволяет по индексу получить необходимый элемент коллекции - объект Error.

У коллекции два метода:

  • Clear - позволяет принудительно очистить содержимое коллекции,
  • Refresh - обновляет коллекцию.
Свойства объекта Error

У объекта Error нет методов и нет событий. У него есть только свойства:

  • Property Description As String (read-only). Задает описание ошибки, которое можно показать пользователю, если не предвидится другой способ обработки ошибки.
  • Property HelpContext As Long (read-only), Property HelpFile As String (read-only). Эти свойства позволяют обратиться за дополнительными разъяснениями к справочной системе, если в ней содержится более подробная информация об ошибке.
  • Property NativeError As Long (read-only). Код ошибки, специфический для данного Провайдера. Полезен, если есть описание ошибок Провайдера.
  • Property Number As Long (read-only). Номер, однозначно идентифицирующий ошибку. Является константой типа HRESULT. Значения соответствуют, но не совпадают со значениями констант, принадлежащих перечислению ErrorValueEnum. Классификация ошибок достаточно подробная, констант в этом перечислении много. Приведу значения лишь некоторых: adErrCantChangeConnection, adErrCantConvertvalue, adErrDataOverflow, adErrFieldsUpdateFailed, adErrInTransaction. Замечу, что, зная номер Number, не просто разобраться, какой тип ошибки имеет место.
  • Property Source As String (read-only). Задает имя объекта, породившего ошибку. В совокупности свойства Source, Number и Description позволяют проанализировать ошибку и, возможно, в диалоге с пользователем устранить ее причину.
  • Property SQLState As String (read-only). Задает 5-и символьный код, соответствующий стандарту ANSI SQL, определяющий состояние SQL, вызвавшее ошибку.
Ольга Гафарова
Ольга Гафарова

Добрый день. Подскажите формулы при решении задачи на рис. 2.2 в лекции №2. Закон Ома, какие должны использоваться формулы для I и R

Курс: Основы офисного программирования и документы Excel

Серегй Лушников
Серегй Лушников