Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   DataSnap mit mehreren Datenbanken (https://www.delphipraxis.net/184436-datasnap-mit-mehreren-datenbanken.html)

Kostas 26. Mär 2015 16:29

Datenbank: Firebird • Version: 2.5 • Zugriff über: DataSnap

DataSnap mit mehreren Datenbanken
 
Hallo Zusammen,

die Frage vorab: Muss die ServerMethods Klasse auch die FDConnection und alle Query beinhaltet
da sie ja je nach LifeCycle für jeden angemeldeten User registriert und instanziiert wird?

Mein DataSnap Server (Delphi XE7) ist verbunden mit drei Firebird 2.5 Datenbanken über FireDac.
Der Server wird maximal bis zu 50 User gleichzeitig bedienen. Die meiste Zeit wird er jedoch
auf Clients warten und sich langweilen.

Die FDConnection und dessen Querys habe ich pro Datenbank separat in je ein DataModul gepackt.
Zusätzlich gibt es für jede Datenbank ein eigenes ServerMethods DataModul. Dieses beinhaltet nur die
Methoden die für die Clients gedacht sind, also ohne die Datenbankkomponenten!

Ich habe es probiert und es funktioniert alles einwandfrei. Sicher bin ich mir jedoch nicht ob es nur ein
Zufall ist dass kein Problem entstanden sind. Ich habe ein weiteres DataSnap Projekt welches in dem
ServerMethods DataModul auch die FDConnection und alle Query beinhaltet. Das funktioniert auch einwandfrei.

Gruß Kostas

Uwe Raabe 26. Mär 2015 16:56

AW: DataSnap mit mehreren Datenbanken
 
Da der DataSnap-Server für mehrere Clients auch mehrere Threads aufmachen kann, muss man bei den Connections schon etwas aufpassen. Am einfachsten lässt sich das realisieren, wenn die Connection im ServerMethods-Datenmodul steckt, aber das ist auch nicht sonderlich performant und verbraucht auch reichlich Connections. Alternativ kann man die Connections auch thread-bezogen poolen.

Edit: Vielleicht mal hier ein bisschen nachlesen: Multithreading_(FireDAC)

himitsu 26. Mär 2015 16:57

AW: DataSnap mit mehreren Datenbanken
 
Nein, in den ServerMethods steckt ja erstmal nur der Zugriffscode für die Clientenanfragen-
Von wo letztendlich die ausgelieferten Daten kommen, ist dabei erstmal egal.

Du mußt nur eventuell auf das Threading achen, also daß alles Threadsave ist,



Ich hatte damals den autogenerierten Servercode genommen, das selber in eigene Klassenstrukturen umkopiert und bissl angepasst. (genauso clientseitig)
z.B. Streams übertragen ist im DataSnap etwas heikel. (vorallem mit über 64 Kilobytechen)

* Es gibt im Server nur eine Instanz für alle (lifecycle = server)
* Bei Abfragen der Clienten wird das gewünschte DataSet aus einer globalen Liste gezogen, kurz gesperrt (Critical Section) kopiert und anschließend in Ruhe zum Clienten übertragen
* Dabei werden auch paar Problemfälle behandelt, wie z.B. daß es bei TEXT knallt (beim Kopieren wird einfach ein größeres VARCHAR daraus gemacht)
* bzw. seit heute wird in der Kopie auch noch eine Spalte clientspezifich angepasst (Werte geändert, bzw. eigentlich eine Art clientabhängiges CalcField angehängt)

Der Server wird als DB-Cache benutzt, für längere SQL, die häufig von Vielen abgerufen werden und praktisch fast überall das Selbe enthalten (bis auf ein paar Felder für die clientseitige Gridfilterung "ist meine", was bissl schwer zu lösen ging, da man GridFilter nicht speichern und vorallem synchronisieren kann, wenn darin "Zuständiger = HierMeinName" geprüft werden soll)
Und dann macht der DataSnap-Server noch paar weitere Dinge, wie ein eigener WebServer für die Hilfedateien (die Sicherheitsrichtlinien vom MS im Intranet sind echt besch***), Dateiserver für's DMS, EDI-Exporte, Backups usw.

Zitat:

verbraucht auch reichlich Connections
Und RAM.

Uwe Raabe 26. Mär 2015 17:04

AW: DataSnap mit mehreren Datenbanken
 
Zitat:

Zitat von himitsu (Beitrag 1294956)
Du mußt nur eventuell auf das Threading achen, also daß alles Threadsave ist,

Das heißt bei FireDAC aber, daß immer nur ein Thread eine Verbindung verwenden darf! So ganz trivial ist das also nicht.

Gemein ist nur, daß das in den simplen Beispielen und Versuchen (mit einem Client oder mehreren nacheinander) immer gut funktioniert.

himitsu 26. Mär 2015 17:24

AW: DataSnap mit mehreren Datenbanken
 
Jupp, daher machen wir das übertragen der DataSets halt "manuell".

CriticalSections und Dergleichen bekommt man nicht in den Code, da die Daten erst nach Ende der Servermethoden übertragen werden und bei DataSets auch noch stückchenweise. (nur so viel, wie der Client grade braucht)
Daher erstellen wir und ein ClientDataSet/MemDataset, nutzen den DataSnapReader zum Kopieren der FeldStruktur und der Inhalte,
dann wird das DataSet übertragen und dem DataSnap wird mitgeteilt, daß es Owner über die Kopie ist ... wird dann von dem freigegeben, wenn fertig.

So können wir die Zugriffe problemlos selber synchronisieren.



Aber wem das zuviel ist, der legt die DataSets/Queries auf die DataSnapModule, lässt für jeden Clienten eine eigene Instanz erstellen. Die Connection kommt da auch mit drauf, wenn sie kein Multithread (Connectionpooling) kann ... dann ist jeder Thread alleine für sich und es gibt auch keine Probleme mehr (außer vielleicht beim RAM)

DataSnap ist (zumindestens im XE) erstaunlich resourcenhungrig, so daß schnell der RAM voll ist, vorallem in einem 32-Bit-Programm. :stupid:

jaenicke 26. Mär 2015 19:25

AW: DataSnap mit mehreren Datenbanken
 
Zitat:

Zitat von himitsu (Beitrag 1294970)
DataSnap ist (zumindestens im XE) erstaunlich resourcenhungrig, so daß schnell der RAM voll ist, vorallem in einem 32-Bit-Programm. :stupid:

Wir benutzen Datasnap ebenfalls mit einer FireDAC Connection pro Thread. Bei uns geht es um Systeme mit mehreren hundert parallel verbundenen Clients. Da haben wir relativ schnell auf 64-Bit umgestellt, alles andere machte gar keinen Sinn. Bei etwa 60 Clients mit diversen abgefragten Datenbanken gab es zwar keine Leistungsprobleme, aber der Speicher war schlicht bei 32-Bit voll. Bei 150 verbundenen Clients waren wir dann schon z.B. bei 4,5 GiB RAM (und der SQL Server bei 10 GiB). Zum Glück ist das heute keinerlei Problem mehr...

Kostas 27. Mär 2015 12:00

AW: DataSnap mit mehreren Datenbanken
 
Vielen vielen Dank für die ausführlichen Infos.

habe ich das jetzt wirklich richtig verstanden, wenn der DataSnap Server als 32Bit kompiliert wird,
benötigt DataSnap mehr Speicher als wenn er als 64Bit kompiliert werden würde? Wenn ja, wie hängt das zusammen?


Zum eigentlichen Problem, wenn bei 150 User "nur" 15-20GB RAM verbraten werden, würde mich
das noch nicht aus der Ruhe bringen. Den RAM kann ich wirklich ignorieren.

Mein DataSnap Server wird jetzt nicht so umfangreich aber ein paar Dutzend Querys werden das schon. Ich wollte einfach nur die Querys thematisch auf mehrere DataModule verteilen wegen der Wartbarkeit. Bei Anwendungen mit ein paar hundert Querys alles auf das ServerMethods Datamodul zu packen ist zwar möglich aber eben danach nur sehr schwer wartbar.

In diesem Projekt mit den drei Datenbanken habe ich eh drei TDSServerClass die auch drei ServerMethod Klassen registriert, und wie ich das Verstanden habe bei LifeCycle=Session werden alle drei
ServerMethod Module instanziiert pro Session. Der Zugriff von den Public ServerMethods auf die Querys
währe damit sichergestellt.


Wäre es denkbar dass man nur wegen der Wartbarkeit weitere ServerMethods DataModule anlegen und die
Querys sachlich trennt, sie würde ja alle mit instanziiert pro Session?


Eigentlich wollte ich in den ServerMethods ausschließlich Methoden für die Clients bereitstellen.
Die sonstige Logik sollte eben auch mehrere weitere DataModuls aufgeteilt werden. Aktuell funktioniert
es so aber ihr meint, es wird Probleme geben.


Wie könnte ich bitte vom ServerMethods ThreadSave auf meine Methode im DataModul zugreifen,
etwa so? Das wäre zu einfach :-)

Delphi-Quellcode:

function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  TThread.Synchronize(nil, procedure
    begin
      LDataSet := dmDALZMI.GetBauDataSet(BauNr);
    end);
  Result := LDataSet;
end;



Aktuell habe ich in ein DataModul das:
Delphi-Quellcode:
function TdmDALZMI.GetBauDataSet(BauNr:integer): TDataSet;
begin
  qrGetBau.Active := False;
  qrGetBau.Params[0].Value := BauNr;
  qrGetBau.Active := True;
  result := qrGetBau;
end;
Im ServerMethods DataModul das:

Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSet(BauNr: integer): TDataSet;
begin
  result := dmDALZMI.GetBauDataSet(BauNr);
end;




Gruß Kostas

mkinzler 27. Mär 2015 12:09

AW: DataSnap mit mehreren Datenbanken
 
Zitat:

habe ich das jetzt wirklich richtig verstanden, wenn der DataSnap Server als 32Bit kompiliert wird,
benötigt DataSnap mehr Speicher als wenn er als 64Bit kompiliert werden würde? Wenn ja, wie hängt das zusammen?
Nein, aber man kann weniger Speicher adressieren

himitsu 27. Mär 2015 13:28

AW: DataSnap mit mehreren Datenbanken
 
Jupp, mit 32 Bit ist nach 2 GB Schluß (3,8 GB mit entsprechenden Hacks und wenn alles dafür kompatibel ist)

Zitat:

Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  TThread.Synchronize(nil, procedure
    begin
      LDataSet := dmDALZMI.GetBauDataSet(BauNr);
    end);
  Result := LDataSet;
end;

Hier ist nur der Zugriff auf GetBauDataSet abgesichert, aber der Zugriff auf das DataSet ist nicht sicher, da der erst nach dem Function-End im Code vom DataSnap für eine ungewisse Dauer auftritt. (mindestens solange die Übertragung dauert und maximal, solange die Kopie im Client existiert und eine Verbindung zum Apps aufrecht erhält ... z.B. Delayed-Loading oder eine bidirektionale Verbindung)

Drum auch meine Lösung mit dem umkopieren. Das kopierte DataSet wird dann nur für diesen einen Aufruf verwendet und ist somit threadsave (wenn die verwendete TDataSet-Implementation nicht dennoch irgendwelche threadunsicheren globalen Dinge veranstaltet)



Grundsätzlich sicher ist nur, wenn sich DS ums Freigeben kümmert, wenn es nichts Globales im Apps gibt (jede Connection bekommt was Eigenes), bzw. wenn man alles Globales selber "vollständig" absichern kann, und wenn die DataSets auch nicht global sind, sondern werden immer neu erstellt, für die jeweilige Anfrage.

Uwe Raabe 27. Mär 2015 14:02

AW: DataSnap mit mehreren Datenbanken
 
Zitat:

Zitat von Kostas (Beitrag 1295057)
Mein DataSnap Server wird jetzt nicht so umfangreich aber ein paar Dutzend Querys werden das schon. Ich wollte einfach nur die Querys thematisch auf mehrere DataModule verteilen wegen der Wartbarkeit. Bei Anwendungen mit ein paar hundert Querys alles auf das ServerMethods Datamodul zu packen ist zwar möglich aber eben danach nur sehr schwer wartbar.

In diesem Projekt mit den drei Datenbanken habe ich eh drei TDSServerClass die auch drei ServerMethod Klassen registriert, und wie ich das Verstanden habe bei LifeCycle=Session werden alle drei
ServerMethod Module instanziiert pro Session. Der Zugriff von den Public ServerMethods auf die Querys
währe damit sichergestellt.


Wäre es denkbar dass man nur wegen der Wartbarkeit weitere ServerMethods DataModule anlegen und die
Querys sachlich trennt, sie würde ja alle mit instanziiert pro Session?


Eigentlich wollte ich in den ServerMethods ausschließlich Methoden für die Clients bereitstellen.
Die sonstige Logik sollte eben auch mehrere weitere DataModuls aufgeteilt werden. Aktuell funktioniert
es so aber ihr meint, es wird Probleme geben.

Du kannst die Querys problemlos auf mehrere TDataModule-Klassen (nicht ServerMethods-Module!) aufteilen. Du musst diese halt jeweils zusammen mit der Instanz des ServerMethod-Moduls instantiieren und eine Connection thread-sicher (z.B. über den Connection-Pool wie in dem verlinkten Artikel) zuweisen. Damit entfällt halt die (leider zu bequeme) Möglichkeit, die Verknüpfungen der Komponenten zwischen verschiedenen Datenmodulen schon während des Designs herzustellen. Es muss halt für jede Instanz eines ServerMethod-Moduls auch der ganze Rattenschwanz der Query-Datenmodule instanziert werden.

Wenn du ein TDSServerModule verwendest und mit TDataSetProvider-Instanzen arbeitest, kannst du die jeweiligen DataSetProvider auf den zusätzlichen Datenmodulen über RegisterProvider dem TDSServerModule bekannt machen. Dann sind die im Client hinterher auch wiederzufinden.

Kostas 27. Mär 2015 14:10

AW: DataSnap mit mehreren Datenbanken
 
Hallo himitsu,

oh man das klinkt wirklich kompliziert. :-(
Wäre das eine Option?

Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  TThread.Synchronize(nil, procedure
    var qrGetBau: TFDQuery;
    begin
      try
        qrGetBau:= TFDQuery.Create(nil);
        qrGetBau.Connection := dmZIMPool.conZMI;

        qrGetBau.SQL.Add('SELECT *');
        qrGetBau.SQL.Add('FROM BAU');
        qrGetBau.SQL.Add('WHERE BAUNR = :BAUNR');
        qrGetBau.Params[0].AsInteger := BauNr;

        qrGetBau.open;
        LDataSet := qrGetBau;
      finally
//        qrGetBau.Free;
      end;
    end);
  Result := LDataSet;
end;

Kostas 27. Mär 2015 14:15

AW: DataSnap mit mehreren Datenbanken
 
[QUOTE=Uwe Raabe;1295081]
Zitat:

Zitat von Kostas (Beitrag 1295057)

Du kannst die Querys problemlos auf mehrere TDataModule-Klassen (nicht ServerMethods-Module!) aufteilen. Du musst diese halt jeweils zusammen mit der Instanz des ServerMethod-Moduls instantiieren und eine Connection thread-sicher (z.B. über den Connection-Pool wie in dem verlinkten Artikel) zuweisen. Damit entfällt halt die (leider zu bequeme) Möglichkeit, die Verknüpfungen der Komponenten zwischen verschiedenen Datenmodulen schon während des Designs herzustellen. Es muss halt für jede Instanz eines ServerMethod-Moduls auch der ganze Rattenschwanz der Query-Datenmodule instanziert werden.

Wenn du ein TDSServerModule verwendest und mit TDataSetProvider-Instanzen arbeitest, kannst du die jeweiligen DataSetProvider auf den zusätzlichen Datenmodulen über RegisterProvider dem TDSServerModule bekannt machen. Dann sind die im Client hinterher auch wiederzufinden.

Hallo Uwe,

das ist doch schon mal ein Lichtblick.
Warum gibt es keine Bücher über DataSnap. Das ist doch ein grosses Thema. :-(
Dankeschön Uwe.

Gruß Kostas

himitsu 27. Mär 2015 17:50

AW: DataSnap mit mehreren Datenbanken
 
Nach dem End geht dann DataSnap daher und läuft über das DataSet, daher ist alles darin, eventuell dennoch nicht threadsicher.

siehe z.B. Optionen ala Delphi-Referenz durchsuchenTFDAutoFetchAll, wo nach dem Open der DB-Zugrif noch nicht beendet sein kann.

Erstmal das Prinzip, ohne irgendeine Synchronisation (Synchronize oder CriticalSection), was man für threadsichere Dinge ala TFileStream benutzen kann.
Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: Integer): TDataSet;
begin
  Result := TFDQuery.Create(nil);
  try
    Result.Connection := dmZIMPool.conZMI;
    Result.SQL.Add('SELECT *');
    Result.SQL.Add('FROM BAU');
    Result.SQL.Add('WHERE BAUNR = :BAUNR');
    Result.Params[0].AsInteger := BauNr;
    Result.Open;
  except
    FreeAndNil(Result);
    raise;
  end;
end;
Innerhalb mußt du selber die Sicherheit für deine Ressourcen sorgen
und nach dem End übernimmt das DataSnap (wenn es der Owner ist/wird)

Wenn man alles gekapselt hat, dann ginge es auch direkt, da Constructoren sich intern selber absicher. (wenn es knallt, wird automatisch Free Destroy aufgerufen)
Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: Integer): TDataSet;
begin
  Result := TMyQuery.Create(nil, dmZIMPool.conZMI, 'SELECT * FROM BAU WHERE BAUNR = :BAUNR', [BauNr]);
end;
Mit einem Synchronize kann man sich überlegen wie man das absicher. (ersteres, wenn man blind davon ausgeht, daß der Rückweg vom Synchronize immer funktioniert)
Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: Integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  TThread.Synchronize(nil, procedure
    begin
      LDataSet := TFDQuery.Create(nil);
      try
        LDataSet.Connection := dmZIMPool.conZMI;
        LDataSet.SQL.Add('SELECT *');
        LDataSet.SQL.Add('FROM BAU');
        LDataSet.SQL.Add('WHERE BAUNR = :BAUNR');
        LDataSet.Params[0].AsInteger := BauNr;
        LDataSet.Open;
      except
        LDataSet.Free;
        raise;
      end;
    end);
  Result := LDataSet;
end;
Delphi-Quellcode:
function TdmServerMethodsZMI.ZMI_GetBauDataSetThreadSave(BauNr: Integer): TDataSet;
var
  LDataSet: TDataSet;
begin
  LDataSet := nil;
  try
    TThread.Synchronize(nil, procedure
      begin
        LDataSet := TFDQuery.Create(nil);
        LDataSet.Connection := dmZIMPool.conZMI;
        LDataSet.SQL.Add('SELECT *');
        LDataSet.SQL.Add('FROM BAU');
        LDataSet.SQL.Add('WHERE BAUNR = :BAUNR');
        LDataSet.Params[0].AsInteger := BauNr;
        LDataSet.Open;
      end);
  except
    LDataSet.Free;
    raise;
  end;
  Result := LDataSet;
end;
PS: Create gehört natürlich grundsätzlich immer vor das Try, denn wenn es im Create knallt, dann ist im Finally/Except die Variable nicht initialisiert,
was einem der Compiler aber auch sagt, wenn man mal liest was der zu sagen kannt.

Sir Rufo 27. Mär 2015 19:38

AW: DataSnap mit mehreren Datenbanken
 
@himitsu

Warum ein
Delphi-Quellcode:
try .. except
wo ein
Delphi-Quellcode:
try .. finally
reicht?
Vor allem im letzten Beispiel lieferst du bei einer Exception eine ungültige Referenz zurück.

So ist es sauber:
Delphi-Quellcode:
LDataSet := TFDQuery.Create;
try
  ...
  Result := LDataSet;
  LDataSet := nil;
finally
  LDataSet.Free;
end;
;)

himitsu 27. Mär 2015 21:08

AW: DataSnap mit mehreren Datenbanken
 
Weil das logischier und vorallem verständlicher ist?
> Wenn es knallt, dann gibt es wieder frei?

Andersrum muß man vorher erstmal gucken/wissen was damit gemacht wurde (an wen anderes zuweisen und auf nil setzen),
aber auf den ersten Blick heißt es erstmal nur
> Gib es immer frei, selbst wenn es erfolgreich war.

Zitat:

Zitat von Sir Rufo (Beitrag 1295122)
Vor allem im letzten Beispiel lieferst du bei einer Exception eine ungültige Referenz zurück.

Nein, denn bei einer Exception kommt der Code bei der Zuweisung nicht mehr vorbei. :zwinker:

[edit]
Ups, hatte was vergessen, aber das hat ja keiner gemerkt. :angle2:
(Copy&Paste im kleinen DP-Beitragseditor ist das Beste)

Kostas 28. Mär 2015 16:16

AW: DataSnap mit mehreren Datenbanken
 
Hallo zusammen,

ich habe nun ein neues DataSnapServer-/Client Projekt aufgesetzt und dabei das Prinzip Connection Pooling eingesetzt.
Ein DataSet kann auch "ThreadSave" vom Client abgerufen werden. Es scheint alles zu funktionieren.


Wenn der Server ein DataSet bereit stellt, darf die Connection am Ende nicht beenden werden.
Ich darf auch nicht die Connection und die Query am ende auf Free setzen. Ist das so in Ordnung?
Setze ich es auf Free, bekommt der Client nix.

Ist die Methode soweit ausreichend ThreadSave?


Delphi-Quellcode:
function TdmDALZMI.GetBau(BauNr: integer): TDataSet;
var
  oConn: TFDConnection;
  oQuery: TFDQuery;
begin
  oQuery := nil;

  oConn := TFDConnection.Create(nil);
  oQuery := TFDQuery.Create(nil);
  try
    TThread.Synchronize(nil, procedure
    begin
      oConn.ConnectionDefName := ZMIConnectionDefName; //<<<<< Der ConnectionManager möchte unbedingt über ConnectionDefName gebunden werden damit Pooling funktioniert.
      oQuery.Connection := oConn;
      oQuery.FetchOptions.AutoFetchAll := afAll; //<< Alle Record sollen vollständig abgerufen werden

      oQuery.SQL.Text := 'SELECT * FROM BAU WHERE BAUNR = :BAUNR';
      oQuery.Params[0].AsInteger := BauNr;
      oQuery.Open;
//      oConn.Close;    //<<<<<<<<<<<<<<<< Ist das OK?
    end);

    result := oQuery;
  finally
//    oQuery.Free;     //<<<<<<<<<<<<<<<< Ist das OK?
//    oConn.Free;      //<<<<<<<<<<<<<<<<  
  end;

end;

Clientseitig wird das DataSet so abgerufen und in eine TFDMemTable copiert:
Delphi-Quellcode:

procedure TdmZMI.GetBauDataSet(BauNr: integer);
var
  LZMIClient: TdmServerMethodsZMIClient;
  LDataSet: TDataSet;

begin
  LZMIClient := TdmServerMethodsZMIClient.Create(dmClientContainer.CMCSQLConnection.DBXConnection);
  try
    LDataSet := LZMIClient.GetBau(BauNr);
    mtBau.Active := False;
    mtBau.CopyDataSet(LDataSet, [coStructure, coRestart, coAppend]);
    mtBau.Open;
  finally
    LZMIClient.Free;
  end;

end;

Gruß Kostas

himitsu 28. Mär 2015 17:27

AW: DataSnap mit mehreren Datenbanken
 
Die Query kann so vom DataSnap (Result + OwningResults) freigegeben werden,
aber wer gibt die Connection frei?

Entweder du leitest die Query ab und baust die Connection dort ein, oder du benutzt die Query als Owner.
Delphi-Quellcode:
Result := TFDQuery.Create(nil);
try
  oConn := TFDConnection.Create(Result);
  oConn.ConnectionDefName := ZMIConnectionDefName;
  Result.Connection := oConn;
  Result.FetchOptions.AutoFetchAll := afAll;
  Result.SQL.Text := 'SELECT * FROM BAU WHERE BAUNR = :BAUNR';
  Result.Params[0].AsInteger := BauNr;
  Result.Open;
except
  Result.Free; // freigeben, wenn es beim Erstellen und vorallem im Open Probleme gab. (Tabelle/Feld nicht gefunden)
  raise;
end;
Und natürlich darf die Query am Ende nicht freigegeben oder geschlossen werden, denn es soll ja deren Inhalt am Ende noch übertragen werden und dafür mit die Query existieren und vorallem auch Daten enthalten. :stupid:

Kostas 28. Mär 2015 19:25

AW: DataSnap mit mehreren Datenbanken
 
Hallo himitzu,

das mit dem Result als TFDQuery schein nicht zu funktionieren. Der Compiler
meckert, "Symbol Result kann nicht erfasst werden" innerhalb der anonymen Thread Methode.

Delphi-Quellcode:
function TdmDALZMI.GetBau(BauNr: integer): TDataSet;
var
  oConn: TFDConnection;
//  oQuery: TFDQuery;
begin
//  oQuery := nil;
  Result := TFDQuery.Create(nil);

  oConn := TFDConnection.Create(nil);
//  oQuery := TFDQuery.Create(nil);
  try
    TThread.Synchronize(nil, procedure
    begin
      oConn.ConnectionDefName := ZMIConnectionDefName;
      Result.Connection := oConn;  //<<<geht nicht
      Result.FetchOptions.AutoFetchAll := afAll;

      Result.SQL.Text := 'SELECT * FROM BAU WHERE BAUNR = :BAUNR';
      Result.Params[0].AsInteger := BauNr;
      Result.Open;
//      oConn.Close;
    end);

//    result := oQuery;
  except
    result.Free;
//    oConn.Free;
  end;

end;

Wegen dem Connection Objekt, als ich umgestellt habe, bin ich nach dem Beispiel Pooling von FireDAC
vorgegangen. Hier wird eine Connection instanziiert allerdings auch wieder freigeben!

Mit nicht freigeben von Objekten bin ich nicht Sattelfest. Ich kann das noch nicht greifen.
Aber wenn es funktioniert, ist mir das auch recht.
Eigentlich hätte ich erwartet dass das Connection Objekt über den ConnectionManager gemanagt wird.

Delphi-Quellcode:
procedure TConnectThread.Execute;
var
  oConn: TFDConnection;
  oQuery: TFDQuery;
  i: Integer;
begin
  oConn := TFDConnection.Create(nil);
  oQuery := TFDQuery.Create(nil);
  try
    oQuery.Connection := oConn;
    oConn.ConnectionDefName := FForm.cbDB.Text;
    for i := 1 to 50 do begin
      oQuery.SQL.Text := 'select count(*) from {id Region}';
      oQuery.Open;
      oConn.Close;
      Synchronize(FForm.Executed);
    end;
  finally
    oConn.Free;
    oQuery.Free;
  end;
end;

Leider kann ich nicht nachvollziehen wie du es gemeint hast mit Query als Owner.
Wenn es deine Zeit erlaubt, würdest du das bitte kurz beschreiben.


Gruß Kostas

himitsu 28. Mär 2015 20:02

AW: DataSnap mit mehreren Datenbanken
 
Wenn eine Variable nicht erfasst werden kann, dann mit einer TempVariable dazwischen arbeiten.

Mit Owner meinte ich das so
Delphi-Quellcode:
oQuery := TFDQuery.Create(nil); // in der Methode mit Try-Except gesichert und danach wird DateSnap der Owner des Result
oConn := TFDConnection.Create(oQuery); // die FDQuery übernimmt die Kontrolle der Freigabe
Hier kommt doch nur noch ConnectionDefName von außerhalb.
Alles Andere ist intern und wäre somit threadsave.
Wenn der Wert ConnectionDefName aus einem String kommt (die sind beim Lesen threadsave, solange es keine externen Änderungen gibt), dann geht das auch so,
ansonsten müsste man eigentlich nur noch diesen Zugriff absichern.

Kostas 28. Mär 2015 20:44

AW: DataSnap mit mehreren Datenbanken
 
Hallo himitzu,

jetzt läuft alles einwandfrei und Sau schnell. :-)
Ohne die Sache mit dem Owner, konnte ich maximal 50 Anfragen stellen da die Default Connections Anzahl bei
50 steht. Somit ist klar, die Connection waren aktiv! Mit dem Owner gibt es nun keine Limitierung.
Zugleich habe ich die Bestätigung dass das Query Objekt von "Geisterhand" selbstständig eliminiert wird
nach dem Transfer da damit auch die Connection eliminiert wird.

Das ist doch wie Weihnachten und Ostern an einem Tag.
Ich bin dir sehr dankbar und weis das auch zu schätzen.

Gruß Kostas


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:36 Uhr.

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