Announcement

Collapse
No announcement yet.

Neue DB-Session in Thread eröffnen

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Neue DB-Session in Thread eröffnen

    Ich habe folgendes Problem:

    Habe ein Datenmodul mit dem ich vom Hauptprozess aus auf die Access DB zugreife.
    Ich habe aber auch zusätzlich einen externen Thread von dem aus ich auch auf die Daten zugreifen möchte. Das funktioniert aber nicht.
    Somit habe ich nach einer Lösung gesucht. Überall schreibt man ich muß eine neu DB-Session aufmachen. Wie geht das über Code? Ich wäre für jedes Quellcodebeispiel dankbar.

    mit bestem Dank im voraus Haller Hansjörg

  • #2
    Hallo,

    über welchen Weg (BDE, ODBC oder ADO) greift die Anwendung im separaten Thread auf die ACCESS-Datenbank zu? Im Fall der BDE muss eine unabhängige <b>TSession/TDataBase</b>-Instanz für jeden Thread verwendet werden. Im Fall von ADO muss der Thread vor dem ersten Zugriff ein eigenes Apartment über die Win32-API-Funktion <b>CoInitialize</B> anmelden. Erst dann darf jeder Thread eine neue Verbindung über das <b>Connection</b>-Objekt von ADO oder über <b>TADOConnection</b> von ADO Express aufbauen

    Comment


    • #3
      Ich greife auf die Access DB mit der BDE (DAO) zu.
      Das habe ich im Enwicklerforum schon gefunden, daß ich eine unabhängige TSession/TDatabase Instanz für jeden Thread verwenden muß.
      Wie erzeuge ich aber so eine unabhänige Instanz?

      Comment


      • #4
        Hallo Hansjoerg

        Ich weiss nicht ob Dir das was nutzt, aber in der Borland Community habe ich folgenden Artikel gefunden, welcher beschreibt, wie man eine Query in einem eigenen Thread ausführt:

        <a href="http://community.borland.com/article/0,1410,16231,00.html">Performing database queries in a background thread</A>

        Gruss
        Joh

        Comment


        • #5
          Habe extra einen schönen Link gemacht, doch er wurde rausgefiltert :-(
          Hier ist die URL:

          http://community.borland.com/article/0,1410,16231,00.htm

          Comment


          • #6
            Hallo,

            bei der Kombination von BDE + DAO hat man es gleich mit 2 Problemen zu tun. Zum einen muss für die BDE eine eigene TSession/TDatabase-Instanz (Datenmodul) verwendet werden, und zum andern muss für DAO (COM-Objekte) ein eigenes <b>Apartment</b> angemeldet werden.

            Auf der CDROM zu meinem Buch <i>Client/Server-Datenbankentwicklung mit Delphi</i> ist im Verzeichnis <i>Kapitel 18\IBCSDemo</i> ein Beispiel zu finden, wie das Programm über eine zweite TSession/TDatabase-Kombination eine Hintergrundsuche über einen separaten Thread ausführt. Es gibt jedoch auch genügend andere Beispiele hier im Forum.

            Mit einem DAO-Beispiel kann ich nicht dienen, aber für den DAO-Nachfolger <b>ADO</b> habe ich ein Beispiel für den Zugriff aus einem separaten Thread heraus. Wichtig ist, in der TThread-Methode <b>Execute</b> über den Aufruf von <b>CoInitialize(nil);</B> ein neues STA anzumelden (Singlethreaded Apartment):
            <pre>
            unit ADOThreadThread;

            interface

            uses
            Classes, Windows;

            type
            ADOSelectThread = class(TThread)
            private
            { Private-Deklarationen }
            FFrmWnd : HWND;
            FConnStr : String;
            FOrt : String;
            protected
            procedure Execute; override;
            public
            constructor Create(hFrmWnd : HWND; sConnStr, sOrt: String);
            end;

            implementation

            uses ADOInt, ActiveX, SysUtils, ADOThreadFrm;

            { ADOSelectThread }

            constructor ADOSelectThread.Create(hFrmWnd: HWND; sConnStr, sOrt: String);
            begin
            FFrmWnd := hFrmWnd;
            FConnStr := sConnStr;
            FOrt := sOrt;
            FreeOnTerminate := True;
            inherited Create(False);
            end;

            procedure ADOSelectThread.Execute;
            var
            aCN : _Connection;
            aRS : _RecordSet;
            vDummy : OleVariant;
            sCount : String;
            begin
            CoInitialize(nil);
            try
            aCN := CoConnection.Create;
            // Datenbankverbindungs-Pool von ADO, daher keine Nachteile,
            // wenn bei jedem Start die Datenbankverbindung neu geöffnet wird
            aCN.Open(FConnStr, '', '', adConnectUnspecified);
            aCN.Execute('SET LOCK_TIMEOUT 500', vDummy, adExecuteNoRecords);
            aRS := CoRecordSet.Create;
            aRS.CursorLocation := adUseClient;
            aRS.CursorType := adOpenForwardOnly;
            aRS.LockType := adLockReadOnly;
            aRS.Open('SELECT COUNT(*) FROM KUNDEN WHERE ORT = ' + QuotedStr(FOrt),
            aCN, adOpenStatic, adLockReadOnly, adCmdText);
            sCount := IntToStr(aRS.Fields[0].Value);
            aRS.Close;
            aCN.Close;
            PostMessage(FFrmWnd, PM_LABELTEXT, 0, Integer(PChar(sCount)));
            finally
            CoUninitialize;
            end;
            end;

            end.
            </pre&gt

            Comment


            • #7
              Danke für die rasche Hilfe, ich werde das gleich testen und hoffe, daß ich es zum Laufen bringe.

              mfg Hansjör

              Comment


              • #8
                Hallo Herr Kosch,

                Ich habe es jetzt probiert so wie sie es gesagt haben, CoInitialize aufzurufen, und danach eine neue Instanz der Komponenten zu bilden. Bei mir funktioniert es aber nicht.

                Zur Erläuterung:
                Ich verwende ein Datenmodul (Klasse "TUDPDB") dieses Datenmodul beinhaltet eine TDatabase - Komponente die mit einer TSession Komponente verbunden ist. Die Eigenschaft "AutoSessionName" der TSession Komponente ist true.

                <pre>
                var dmcaudp:TUDPDB; //Datenmodul<br>
                begin<br>
                result:=false;<br>
                CoInitialize(nil);<br>
                try<br>

                dmcaudp:=TUDPDB.Create(AAOWNER);<br>
                dmcaudp.dbMain.Connected:=false;<br>
                dmcaudp.dbMain.Params.Clear;<br>
                dmcaudp.dbMain.Params.Add('PASSWORD=' + AdminUDPMDBpwd);<br>
                dmcaudp.dbMain.Params.Add('DATABASE NAME=' + cudpdb);<br>
                dmcaudp.dbMain.Connected:=true;<br>
                finally;<br>
                CoUninitialize;<br>
                end;<br>
                end;<br>
                </pre>

                Hier tritt der Fehler auf!!
                Fehlermedlung:
                Information
                In Projekt xxx.exe trat ein Problem mit folgender Meldung auf: 'Zugriffsverletzung ...'
                Kurioser Weise kann ich nachdem dieser Fehler aufgetreten ist von dieser Delphi Instanz keine Verbindung zur Datenbank mehr herstellen. Ich muß jedesmal Delphi komplett beenden und wieder neu starten, damit ich auf die Datenbank wieder zugreifen kann.

                Was mache ich hier falsch

                Comment


                • #9
                  Zu dem Problem, daß Du Delphi nach diesem Fehler komplett beenden und neu starten mußt, damit Du wieder auf die DB zugreifen kannst, liegt daran, daß Aufgrund des Fehlers von der BDE beim ende deines Programmes die verwendeten Filehandles der BDE nicht mehr freigegeben werden. Diese werden erst dann wieder freigegeben, wenn alle Applikationen, welche die BDE verwenden beendet werden.

                  Du kannst diesen Neustart von Delphi hinauszögern, indem Du die Einstellungen der BDE (Systemsteuerung/BDE-Verwaltung/System/Init) hochstellt (wahrscheinlich genügt es fürs erste den Eintrag MAXFILEHANDLES hochzusetzen)

                  Comment


                  • #10
                    Hallo,

                    man darf CoUninitialize erst dann aufrufen, wenn der Thread wieder beendet wird bzw. wenn danach in diesem Thread niemals mehr auf DAO zugegriffen wird

                    Comment


                    • #11
                      Hallo Herr Kosch,

                      Danke einmal für die Antwort, aber ich glaube das löst mein Problem noch nicht.

                      Der Fehler tritt nämlich auch beim 1. Durchlauf in der Zeile Connected:=true; auf. Kann das wirklich das Problem sein?

                      Der "Thread" in dem ich auf die Datenbank zugreife (mit obiger Routine) ist nicht ein selbstdefinierter, sondern von den INDY 8 Komponenten der TidTelnetServer. Auf die Datenbank zuzugreifen versuche ich da im Ereignis OnAuthentication.

                      Kann das auch ein Problem sein

                      Comment


                      • #12
                        Hallo,

                        es stellt sich dabei die Frage, <b>in welchem Thread</B> das Ereignis OnAuthentication überhaupt abgearbeitet wird. Taucht in dieser INDY-Komponente dabei Synchronize auf

                        Comment


                        • #13
                          Das Ereignis wird nicht im Hauptthread abgearbeitet.
                          Das Ereignis wird aus einer von durch einen Constructor eines Objekt ausgelöst. dieses Objekt wird jedoch aus einem Thread (TidListenerThread) heraus gestartet.
                          Synchronize wird aber in diesem Abschnitt nie verwendet!

                          Comment

                          Working...
                          X