Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi TFDEventAlerter HowTo (https://www.delphipraxis.net/208015-tfdeventalerter-howto.html)

haentschman 28. Mai 2021 07:14

Datenbank: MSSQL • Version: 2017 • Zugriff über: FireDAC

TFDEventAlerter HowTo
 
Hallo...:P

Mein letzter Strohhalm...euch zu fragen. :zwinker:

SOLL:
Die Datenbank "soll" ein Event (pro Tabelle) auslösen. Der Client soll dann seine Datenmenge aktualisieren können.

IST:
Ich habe alles ausprobiert...es wird kein Event ausgelöst. :cry:

Infos:

http://docwiki.embarcadero.com/RADSt...ngen_(FireDAC)
http://docwiki.embarcadero.com/RADSt...ngen_(FireDAC)

Beispiel ausprobiert:
https://stackoverflow.com/questions/...ql-server-2012

Ich habe mich für die Variante "_FD_EVENTS UPDATE notification" entschieden.

1. Broker ist on
2. _FD_EVENTS Tablelle wurde erstellt
3. 'evtUsers' Event wurde als Datensatz in der Tabelle angelegt (über Register der Komponente)
(Die Komponente versucht jedesmal den Datensatz anzulegen -> stille Exception bei Vorhandensein...macht Spaß im Debug. :evil:)
Zitat:

---------------------------
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt Project1.exe ist eine Exception der Klasse EMSSQLNativeException mit der Meldung '[FireDAC][Phys][ODBC][Microsoft][SQL Server Native Client 11.0][SQL Server]Verletzung der PRIMARY KEY-Einschränkung "PK___FD_EVEN__737584F7CB96DC61". Ein doppelter Schlüssel kann in das dbo._FD_EVENTS-Objekt nicht eingefügt werden. Der doppelte Schlüsselwert ist (evtUsers).' aufgetreten.
---------------------------
Anhalten Fortsetzen Hilfe
---------------------------

---------------------------
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt Project1.exe ist eine Exception der Klasse EMSSQLNativeException mit der Meldung '[FireDAC][Phys][ODBC][Microsoft][SQL Server Native Client 11.0]OZum Festlegen dieses Attributs müssen bestimmte andere Attribute festgelegt werden. Einstellung des Attributs wird ignoriert.' aufgetreten.
---------------------------
Anhalten Fortsetzen Hilfe
---------------------------
4. Signal function der Komponente erhöht den Wert des Values
5. TFDPhysMSSQLDriverLink wird der gebraucht? :gruebel:
6. Beim Beenden oder Close kommt eine stille Exception bei der Freigabe des Threads der Komponente. :kotz:
Zitat:

---------------------------
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt Project1.exe ist eine Exception der Klasse $C0000005 mit der Meldung 'access violation at 0x004c893c: read of address 0x0000003c' aufgetreten.
---------------------------
Anhalten Fortsetzen Hilfe
---------------------------
Delphi-Quellcode:
procedure TFDPhysMSSQLEventAlerter.InternalAbortJob;
begin
  FWaitThread.Terminate; // <- hier
  FWaitCommand.AbortJob(True);
end;

Ergebnis:

kein Event wird ausgelöst. :evil:

Was habe ich vergessen? Was ist falsch? :?

Wie kommt denn der Broker von MSSQL ins Spiel? Der Alerter braucht doch nur im Task den Wert des Values vergleichen und das Event auslösen..oder? :gruebel:


Testcode:
Delphi-Quellcode:
procedure TForm1.ale1Alert(ASender: TFDCustomEventAlerter; const AEventName: string; const AArgument: Variant);
begin
  qry1.Refresh;
end;

procedure TForm1.btn1Click(Sender: TObject);
begin
  con1.Connected := True;

  qry1.SQL.Text := 'select * from Users';
  qry1.Open;
end;

procedure TForm1.btn2Click(Sender: TObject);
begin
  con1.Close;
end;

procedure TForm1.btn3Click(Sender: TObject);
begin
  ale1.Signal('evtUsers', 1);
end;
Delphi-Quellcode:
object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 511
  ClientWidth = 933
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  DesignSize = (
    933
    511)
  PixelsPerInch = 96
  TextHeight = 13
  object grd1: TDBGrid
    Left = 32
    Top = 88
    Width = 889
    Height = 401
    Anchors = [akLeft, akTop, akRight, akBottom]
    DataSource = ds1
    TabOrder = 0
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -11
    TitleFont.Name = 'Tahoma'
    TitleFont.Style = []
  end
  object btn1: TButton
    Left = 32
    Top = 40
    Width = 75
    Height = 25
    Caption = 'Open'
    TabOrder = 1
    OnClick = btn1Click
  end
  object btn2: TButton
    Left = 113
    Top = 40
    Width = 75
    Height = 25
    Caption = 'Close'
    TabOrder = 2
    OnClick = btn2Click
  end
  object btn3: TButton
    Left = 264
    Top = 40
    Width = 75
    Height = 25
    Caption = 'btn3'
    TabOrder = 3
    OnClick = btn3Click
  end
  object con1: TFDConnection
    Params.Strings = (
      'DriverID=MSSQL'
      'MARS=No'
      'Server=XXX'
      'User_Name=Blubb'
      'Password=Test'
      'Database=Bla')
    Connected = True
    LoginPrompt = False
    Left = 824
    Top = 24
  end
  object qry1: TFDQuery
    Connection = con1
    Left = 32
    Top = 200
  end
  object ds1: TDataSource
    DataSet = qry1
    Left = 80
    Top = 200
  end
  object ale1: TFDEventAlerter
    Connection = con1
    Names.Strings = (
      'evtUsers')
    SubscriptionName = 'Events'
    Options.AutoRegister = True
    Options.MergeData = dmNone
    Options.AutoRefresh = afNone
    OnAlert = ale1Alert
    Left = 824
    Top = 80
  end
  object lnk1: TFDPhysMSSQLDriverLink
    Left = 760
    Top = 24
  end
end

Uwe Raabe 28. Mai 2021 09:28

AW: TFDEventAlerter HowTo
 
Funktioniert denn das FireDAC.TFDEventAlerter Beispiel?

Falls du die Samples nicht installiert hast: https://github.com/Embarcadero/RADStudio10.3.3Demos

lowmax_5 28. Mai 2021 09:45

AW: TFDEventAlerter HowTo
 
Hi,
ich hatte es vor ein paar Wochen ebenfalls versucht unter MSSQL (2008) den TFDEventAlerter mit den Demos zum laufen zu bekommen und habe alle angegebenen Verfahren getestet. Leider vergebens- das Thema wurde daher erst einmal zurück gestellt. Es wurde nun mit Poll-Verfahren umgesetzt... :(

Unter Firebird hat es nach 5 Min. funktioniert :). Nur die Portfreigabe/Firewall dabei beachten, da auf einem anderen Port <> 3050 kommuniziert wird.

Uwe Raabe 28. Mai 2021 10:41

AW: TFDEventAlerter HowTo
 
Könnte auch mit der verwendeten Delphi-Version zusammenhängen. Bei Sydney gab es da wohl ein paar Verbesserungen. Wenn ich Zeit finde, kann ich das Beispiel ja mal mit 10.4.2 und MSSQL ausprobieren.

haentschman 29. Mai 2021 08:11

AW: TFDEventAlerter HowTo
 
Moin...:P
Zitat:

Wenn ich Zeit finde, kann ich das Beispiel ja mal mit 10.4.2 und MSSQL ausprobieren.
Durch das Rumprobieren habe ich folgendes festgestellt:

Der Alerter legt mit jedem Register den Service und einen Queue (wahrscheinlich wegen der Einstellungen) mit jeweils einer GUID im Namen an. Diese werden beim Beenden wieder entfernt. Soweit so gut. Wenn das Programm aber "abgeschossen" oder über die IDE beendet wird, bleiben die Einträge in den Systemtabellen vorhanden. :roll:

Ist wahrscheinlich technisch nicht anders machbar...:?

PS: Ich hatte auch einmal nur den Namen ohne GUID gesehen...aber nur einmal. :gruebel:

Danke :wink:

PS:
Zitat:

Es wurde nun mit Poll-Verfahren umgesetzt..
...da werde ich mir auch einen Thread basteln der das erledigt. :wink: Ich würde das über eine Tabelle mit GUID, statt BigInt (da ist irgendwann Schluß), lösen. Die Datenmengen sind sogar hardgecodet in der Tabelle registriert. Der Thread schaut...Änderung GUID -> Event. :thumb:
evt. Vorteil: kein Broker benötigt.

himitsu 29. Mai 2021 12:06

AW: TFDEventAlerter HowTo
 
Hast du es mal "manuell" versucht?
(CREATE EVENT NOTIFICATION)

Im Postgres hatten wir auch immer wieder mit dem TPgAlerter Problemchen,
also dass der oftmals einfach garnicht empfing, oder nach einem Reconnect die Arbeit ebenfalls einstellte (obwohl er darauf eigentlich reagieren sollte, so weit ich es im Code sah).

Am Ende hab ich nun im AfterConnect der Connection die Registrierung selbst gemacht (EXECUTE mit einem Registrieungs-Statement)
und dann es kommt nun im OnNotify der TPgConnecton an ... und das nun auch sehr zuverlässig. (bisher keine Probleme mehr bemerkt)

braunbaer 30. Mai 2021 14:43

AW: TFDEventAlerter HowTo
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1490312)
Könnte auch mit der verwendeten Delphi-Version zusammenhängen. Bei Sydney gab es da wohl ein paar Verbesserungen. Wenn ich Zeit finde, kann ich das Beispiel ja mal mit 10.4.2 und MSSQL ausprobieren.

Das wäre super.
Ich sollte dies ebenfalls haben, bringe es aber einfach nicht zum laufen.
Das FDEventAlerter1Alert wird nicht getriggert.

Hoffe, du findest die Ursache...

braunbaer 3. Jun 2021 07:18

AW: TFDEventAlerter HowTo
 
Hat zu diesem Thema jemand eine Lösung?

Mir wäre das viel lieber, als alle paar Sekunden auf neue oder geänderte Daten zu prüfen.

Bin für jeden Tipp oder kleines Beispiel dankbar...

haentschman 3. Jun 2021 07:27

AW: TFDEventAlerter HowTo
 
Hallöle...8-)
Zitat:

Mir wäre das viel lieber, als alle paar Sekunden auf neue oder geänderte Daten zu prüfen.
Das ganze war mir zu unsicher. Ich habe mich fürs "Polling" entschieden...

1. EventTypes
Delphi-Quellcode:
TRefreshDataType = (rdtComplete)
...
2. In der Datenbank eine Tabelle:
Code:
create table _Events
(
  ID int IDENTITY(1,1) NOT NULL,
  EventType int NOT NULL,
  EventName nvarchar(30) NOT NULL,
  EventCurrentGUID uniqueidentifier NOT NULL,
  CONSTRAINT Events_pk PRIMARY KEY CLUSTERED (ID ASC)
)
create index ixEventType on _Events (EventType)
GO

/* Events */
insert into _Events (EventType, EventName, EventCurrentGUID) values (0, 'Complete', NEWID())
GO

/* Trigger*/
create trigger TR_Complete on Complete for insert, update, delete as
begin
  update _Events set EventCurrentGUID = NEWID() where EventType = 0
end
GO
3. Ein Thread der das ganze überwacht (Auszug)
Delphi-Quellcode:
procedure TDatabaseEventThread.Execute;
var
  Qry: TFDQuery;

  procedure LoadEvents;
  begin
    Qry.Open;

    while not Qry.Eof do
    begin
      FGUIDDict.Add(Qry.FieldByName('EventType').AsInteger, TGuidField(Qry.FieldByName('EventCurrentGUID')).AsGUID);
      Qry.Next;
    end;
  end;

  procedure CheckEvents;
  var
    PairCurrent: TPair<Integer, TGUID>;
  begin
    FGUIDDictCurrent.Clear;
    Qry.Close;
    Qry.Open;

    while not Qry.Eof do
    begin
      FGUIDDictCurrent.Add(Qry.FieldByName('EventType').AsInteger, TGuidField(Qry.FieldByName('EventCurrentGUID')).AsGUID);
      Qry.Next;
    end;

    for PairCurrent in FGUIDDictCurrent do
    begin
      if FGUIDDict.ContainsKey(PairCurrent.Key) then // sicher ist sicher
      begin
        if not (FGUIDDict.Items[PairCurrent.Key] = PairCurrent.Value) then
        begin
          FGUIDDict.Items[PairCurrent.Key] := PairCurrent.Value; // zurückschreiben
          FLastEvent := TRefreshDataType(PairCurrent.Key); // DataType für Synchronisierung
          SyncOnDataChange;
        end;
      end;
    end;
  end;

begin
  inherited;
  Qry := FDatabase.CreateQuery;
  try
    Qry.SQL.Text := FDatabase.GetSQLByName('DB_EVENTS');

    LoadEvents;

    while not Self.Terminated do
    begin
      if FDatabase.IsEventDataChangeActive then
      begin
        CheckEvents;
      end;
      Sleep(1000);
    end;
  finally
    Qry.Free;
  end;
end;
Wenn ein Event eintritt, verteile ich das über eine Message an die Forms und die gibt das an die entsprechende Formlogik weiter. Die Formlogik kennt die Events auf die sie reagieren soll und aktualisiert ggf. die Datenmenge der Form.

...fertsch. :wink:


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:43 Uhr.

Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf