Не удалось получить образец Delphi EventAlerter, работающий с SQL Server 2012
Мне нужно обнаружить изменения в базе данных, и я подумал, что компонент TFDEventAlerter будет хорошим вариантом. Пример приложения, поставляемого с Delphi 10.2 Tokyo, у меня не работает. Это работает для других типов баз данных. Нажатие на кнопку Fire Events не обнаруживается при подключении к MS SQL. Кто-нибудь может предложить какие-либо изменения, чтобы заставить это работать? Кроме того, вы можете указать мне рабочий пример этого компонента с SQL Server?
1 ответ
FireDAC предоставляет два способа использования уведомлений о запросах SQL Server. Либо вы позволяете FireDAC создать таблицу _FD_EVENTS, за которой отслеживается UPDATE значения поля в строке, вставляемой для каждого зарегистрированного события [1] (раздел 2.1.), Либо задаете запрос SELECT, чей набор результатов будет отслеживаться для любого UPDATE. с момента регистрации события [1] (раздел 2.2.).
Для использования уведомлений о запросах SQL Server, реализованных FireDAC в целом, вы должны сначала включить их для используемой базы данных (раздел 1.).
1. Включить уведомления
Для уведомлений о запросах, которые реализует FireDAC, вы должны включить компонент Service Broker в базе данных, к которой вы будете подключены. Чтобы проверить, включен ли компонент Service Broker для вашей базы данных, вы можете выполнить запрос следующим образом (с возможными результатами 0= отключено, 1= включено):
SELECT is_broker_enabled FROM sys.databases WHERE name = 'MyDatabase'
Если он отключен, выполните этот запрос:
ALTER DATABASE MyDatabase SET ENABLE_BROKER;
Или, если у вас будут какие-то ожидающие транзакции и выполнение вышеуказанного запроса занимает много времени, вы можете откатить все ожидающие транзакции и немедленно ИЗМЕНИТЬ вашу базу данных:
ALTER DATABASE MyDatabase SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE;
Хорошо, теперь давайте предположим, что у вас включен компонент Service Broker для вашей базы данных, и у вас достаточно прав доступа для подписки на уведомления.
2.1. _FD_EVENTS ОБНОВЛЕНИЕ уведомления
Когда событие, определенное в формате
Итак, если вы определите свое событие так:
FDEventAlerter1.Names.Clear;
{ create temporary, uniquely named queue and service }
FDEventAlerter1.Names.Add('QUEUE=?');
FDEventAlerter1.Names.Add('SERVICE=?');
{ define event in format '<message>', where:
<message> - event name (passed as AEventName param of the OnAlert event) }
FDEventAlerter1.Names.Add('MyEvent');
Вы можете вызвать событие, например, выполнив запрос, подобный этому (на самом деле, даже значение BIGINT может переполниться запросом, подобным этому, так что остерегайтесь использовать его в рабочем коде):
UPDATE _FD_EVENTS SET Value = Value + 1 WHERE Name = 'MyEvent';
События, определенные в формате
2.2. Запрос результатов обновления ОБНОВЛЕНИЕ
Другим способом является просмотр любого ОБНОВЛЕНИЯ определенного результирующего набора, возвращаемого данным запросом SELECT во время регистрации события [1].
Давайте создадим такую таблицу с двумя вставленными строками:
CREATE TABLE Customers
(
ID INT IDENTITY(1, 1) PRIMARY KEY NOT NULL,
FirstName VARCHAR(50) NOT NULL
);
INSERT INTO Customers (FirstName) VALUES ('John');
INSERT INTO Customers (FirstName) VALUES ('Alice');
Теперь, чтобы получить уведомление при изменении значения поля FirstName любой из этих двух строк, определите формат события следующим образом, прежде чем зарегистрировать событие [1]:
FDEventAlerter1.Names.Clear;
{ create temporary, uniquely named queue and service }
FDEventAlerter1.Names.Add('QUEUE=?');
FDEventAlerter1.Names.Add('SERVICE=?');
{ define event in format 'CHANGE<index>=<message>;<SELECT query>', where:
<index> - event index
<message> - event name (passed as AEventName param of the OnAlert event)
<SELECT query> - SELECT query whose resultset will be watched for UPDATE
changes; when this resultset changes, the event fires }
FDEventAlerter1.Names.Add('CHANGE1=MyEvent;SELECT FirstName FROM Customers');
Для запуска события вы можете затем обновить значение поля FirstName любой строки в наборе наблюдаемых результатов, например:
UPDATE Customers SET FirstName = 'Johnny' WHERE ID = 1;
Или же:
UPDATE Customers SET FirstName = 'Alicia' WHERE ID = 2;
Или же:
UPDATE Customers SET FirstName = 'Robert' WHERE ID = 1;
UPDATE Customers SET FirstName = 'Cathie' WHERE ID = 2;
Но вы не будете уведомлены, когда вы ВСТАВИТЕ новую строку и ОБНОВИТЕ ее впоследствии после регистрации события [1], независимо от того, что определенный набор результатов будет фактически включать такую строку. Таким образом, вы не получите уведомление:
INSERT INTO Customers (FirstName) VALUES ('Barry');
UPDATE Customers SET FirstName = 'Barrie' WHERE ID = SCOPE_IDENTITY();
Это означает, что вы на самом деле создаете такой результат, который просматривается. То же самое происходит и в случае событий, определенных в формате
3. Минимальный пример
Вот минимальный пример использования формата
procedure TForm1.ButtonStartClick(Sender: TObject);
begin
FDEventAlerter1.Names.Clear;
{ create temporary, uniquely named queue and service }
FDEventAlerter1.Names.Add('QUEUE=?');
FDEventAlerter1.Names.Add('SERVICE=?');
{ define event in format '<message>', where:
<message> - event name (passed as AEventName param of the OnAlert event) }
FDEventAlerter1.Names.Add('MyEvent');
{ there's only one for SQL Server, <default> should equal to this by now }
FDEventAlerter1.Options.Kind := 'QueryNotifies';
{ if Timeout > 0 and no event arrives in this number of milliseconds, the
OnTimeout event is fired, we can use it as "heartbeat" }
FDEventAlerter1.Options.Timeout := 10000;
{ execute event handlers in the main thread }
FDEventAlerter1.Options.Synchronize := True;
{ start watching }
FDEventAlerter1.Active := True;
end;
procedure TForm1.FDEventAlerter1Alert(ASender: TFDCustomEventAlerter;
const AEventName: string; const AArgument: Variant);
begin
{ if AEventName matches <message> from the event definition }
if SameText(AEventName, 'MyEvent') then
ShowMessage('MyEvent fired!');
end;
procedure TForm1.FDEventAlerter1Timeout(Sender: TObject);
begin
ShowMessage('No event was fired in 10 seconds!');
end;
А теперь попробуйте вызвать вышеуказанное событие. Выполните это на SQL Server:
UPDATE _FD_EVENTS SET Value = Value + 1 WHERE Name = 'MyEvent';
Вызов метода Signal из кода должен привести к тому же результату, что и выполнение вышеуказанного запроса, сигнальное событие.
[1] - под регистрацией события подразумевается либо вызов метода Register, либо включение свойства Active.