34.Работа с событиями

34.1. Понятие события

В SQL-языке InterBase определен оператор

POST_EVENT "Имя события";

После его выполнения наступает событие с данными именем события. Сервер БД уведомляет о наступлении события все активные приложения, зарегистрировавшие свой интерес к данному событию посредством выполнения оператора EVENT INIT. Эти операторы мы изучать не будем по той простой причине, что в клиентских приложениях, написанных на Delphi, они неявно выполняются компонентом TIBEventAlerter.

Событие представляет собой уведомление о наступлении определенной ситуации и посылается сервером БД всем клиентским приложениям, которые зарегистрировались как получатели данного события.

Обмен событиями сервера и приложения часто очень важен и может широко использоваться. Например, пусть клиентское приложение должно обновлять НД после определенного количества изменений (например, 10 или 100), внесенных в таблицу БД одновременно работающими с ней пользователями. Тогда такое обновление может производиться приложением после получения 10(100) уведомлений от сервера о наступлении события изменения БД другими приложениями.

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

34.2. Приложения Delphi и компонент TIBEventAlerter

Компонент TIBEventAlerter расположен в палитре компонентов на странице Samples. Тот факт, что он расположен не на страницах Data Contras или Data Access, можно объяснить специализацией компонента на работу с Borland InterBase.

Для клиентских приложений, работающих с Borland InterBase, этот компонент, во-первых, регистрирует на сервере приложение как приемник определенных событий, и во-вторых, позволяет эти события обрабатывать. В компоненте TIBEventAlerter определены следующие свойства, методы и события.

property Database: TDatabase; - содержит имя компонента TDatabase, управляющего соединением с БД, работающей под управлением Borland InterBase.

property Events: TStrings; - определяет список событий, о наступлении которых сервер БД будет информировать клиентское приложение.

property Registered: Boolean; - возвращает True, если программа зарегистрирована как приемник сообщений о наступлении событий, определяемых свойством Events.

procedure RegisterEvents; - выполняет регистрацию приложения;

procedure UnregisterEvents; - отключает регистрацию приложения как приемника сообщений. Данный метод не может применяться в обработчике события OnEventAlert. Перед выполнением метода в обработчике OnEventAlert полезно установить изменяемый параметр CancelAlerts в False. procedure OnEventAlert: TEventAlert; - обработчик события TEventAlert;

TEventAlert = procedure( Sender: TObject; EventName: String;

EventCount: longint; var CancelAlerts: Boolean);

Назначение параметров:

EventName - содержит имя события, посланного сервером приложению клиента;

EventCount - содержит количество событий (типа, определенного параметром EventName), имевших место на сервере с момента последней передачи клиентскому приложению уведомления о наступлении событий;

CancelAlerts - изменяемый параметр; значение True (по умолчанию) сообщает серверу о том, что клиентское приложение продолжает интересоваться событиями из списка, указанного в свойстве Events; False сообщает о том, что интерес приложения к уведомлению о событиях иссяк.

34.3. Использование компонента TIBEventAlerter для обработки событий сервера БД в клиентском приложении

Пусть в БД, расположенной на сервере, для таблицы БД RASHOD определены триггеры

CREATE TRIGGER AIEVENT_RASHOD FOR RASHOD

ACTIVE

AFTER INSERT

AS

BEGIN

POST_EVENT "INS_POSTED";

END

CREATE TRIGGER AUEVENT_RASHOD FOR RASHOD

ACTIVE

AFTER UPDATE

AS

BEGIN

POST_EVENT "UPD_POSTED";

END

CREATE TRIGGER ADLEVENT_RASHOD FOR RASHOD

ACTIVE

AFTER DELETE

AS

BEGIN

-POST_EVENT "DEL_POSTED";

END

Для регистрации указанных сообщений в клиентском приложении разместим компонент TIBEventAlerter с именем IBEventAlerterl. Укажем в его свойстве Database имя компонента TDatabase, управляющего соединением с удаленной БД. Установим свойство этого компонента Registered в True. В списке свойства Events определим события, при уведомлении о наступлении которых с сервера, в клиентском приложении должны предприниматься какие-либо действия (рис. 34.1).

Определим обработчик события OnEventAlert. Пусть нам необходимо, чтобы через каждые десять изменений происходило обновление набора данных RashodQuery (компонент TQuery), ассоциированного с таблицей RASHOD. Это важно, поскольку клиентское приложение "не видит" изменений в БД. сделанных другими приложениями.

procedure TFormI.IBEventAlerterlEventAlert(Sender: TObject;

EventName: string; EventCount: Longint; var CancelAlerts:

Boolean) ;

const GettingEventsCnt : Integer = 0;

STEP = 10;

Period : Integer = STEP;

begin

GettingEventsCnt := GettingEventsCnt + EventCount;

IF GettingEventsCnt > Period THEN begin

RashodQuery.Close;

RashodQuery.Open;

Period := GettingEventsCnt + STEP;

END;//if

34.4. Обмен сообщениями между приложениями

Пусть одновременно должны работать два клиентских приложения А и В, использующие одну и ту же удаленную БД. В процессе работы приложение В шлет сообщение приложению А, а то, в свою очередь, шлет приложению В подтверждение приема посланного сообщения.

Определим в удаленной БД следующие процедуры:

CREATE PROCEDURE B_SEND_INIT

AS

BEGIN

POST_EVENT "B_LOADED";

END

CREATE PROCEDURE A_SEND_INIT

AS

BEGIN

POST_EVENT "A_LOADED";

END

В форме приложения В разместим компоненты DatabaseB, IBEventAlerterB, StoredProcB (рис. 34.2).

Компонент DatabaseB управляет соединением приложения В с удаленной БД, общей для приложений А и В. Компонент StoredProcB предназначен для вызова хранимой процедуры B_SEND_INIT после нажатия кнопки "Послать сообщение к А":

procedure TFormB.SendButtonClick(Sender: TObject);

begin

DataBaseB.StartTransaction;

StoredProcB.ExecProc;

DataBaseB.Commit;

end;

ЗАМЕЧАНИЕ. Как можно заметить, вызов хранимой процедуры на сервере должен происходить в рамках подтвержденной транзакции:

сервер рассылает зарегистрировавшимся для получения сообщений клиентам сообщение о наступлении события только после завершения транзакции, внутри которой возбуждается событие.

Компонент IBEventAlerterB отслеживает получение от сервера БД сообщения о наступлении события "A_LOADED". В этом случае просто визуализируется полученное сообщение в компоненте ListBoxB:

procedure TFormB.IBEventAlerterBEventAlert(Sender: TObject;

EventName: string; EventCount: Longint; var CancelAlerts:

Boolean) ;

begin

ListBoxB.Items.Add(EventName) ;

end;

В форме приложения А (рис. 34.3) размещены компоненты DatabaseA, IBEvenlAlerterA, StoredProcA.

Компонент DatabaseA управляет соединением приложения А с удаленной БД, общей для приложений А и В. Компонент StoredProcA предназначен для вызова хранимой процедуры A_SEND_INIT. Этот вызов происходит после получения компонентом IBEventAlerter А уведомления о наступлении события "B_LOADED":

procedure TFormA.IBEventAlerterAEventAlert(Sender: TObject;

EventName: string; EventCount: Longint; var CancelAlerts:

Boolean);

begin

ListBoxA.Items.Add(EventName) ;

DataBaseA.StartTransaction;

StoredProcA.ExecProc;

DataBaseA.Commit;

end;

34.5. Обмен инициализирующими сообщениями между приложениями

Несколько усложним предыдущий пример. Пусть с удаленной БД одновременно работают приложения А и В, причем приложение А должно быть загружено в момент начала работы приложения В. Тогда приложение В должно проверить факт загрузки приложения А и, если оно не загружено, перейти в состояние ожидания. Работа приложением В может быть продолжена только после загрузки приложения А.

В этом случае приложение В должно претерпеть некоторые изменения. Отсылка сообщения к приложению А и ожидание поступления от него подтверждающего сообщения вынесена в отдельную форму WailingForm. В ней размещены компоненты DatabaseB, StoredProcB, Timer 1 и IBEventAlerterB (рис. 34.4).

Диалоговое окно "Ожидание ответа от приложения А"

Рис. 34.4. Форма WailingForm

Компонент DatabaseB управляет соединением приложения В с удаленной БД, общей для приложений А и В. Компонент StoredProcB предназначен для вызова хранимой процедуры B_SEND_INIT. Транзакция вызова хранимой процедуры осуществляется каждые 5 секунд компонентом Timer! (тип TTimer) в обработчике события On Timer:

procedure TWaitingForm.TimerlTimer(Sender: TObject);

begin

DatabaseB.StartTransaction;

StoredProcB.ExecProc;

Databases.Commit;

end;

Повторяющийся вызов хранимой процедуры необходим, чтобы приложение А, если оно загружается позднее приложения В, наверняка получило от сервера уведомление о наступлении события "B_LOADED". Как только компонент IBEvenlAlerierB получит от сервера обратное инициализирующее сообщение, посланное приложением А, компонент Timer} дезактивизируется и повторяющийся вызов хранимой процедуры B_SEND_INIT будет прекращен, программа сообщает серверу о том, что событие "A_LOADED" ее больше не интересует, а форма WailingForm закрывается:

procedure TWaitingForm.IBEventAlerterBEventAlert(Sender: TObject;

EventName: string; EventCount: Longint; var CancelAlerts: Boolean);

begin

Timer1.Enabled := False;

FormB.ListBoxB.Items.Add(EventName) ;

CancelAlerts := True;

WaitingForm.ModalResult := mrOk;

end;

Перед вызовом из главной формы приложения В форма WaitingForm динамически создается, а после окончания работы - уничтожается:

procedure TFormB.FormActivate(Sender: TObject);

begin

WaitingForm := TWaitingForm.Create(Self);

WaitingForm.ShowModal;

WaitingForm.Free;

end;

В форме WaitingForm отменены иконки выхода, минимизации и максимизации (свойство Border-Icons), чтобы предотвратить выход из формы до получения инициализирующего сообщения от приложения А.