Announcement

Collapse
No announcement yet.

ADO im Thread benutzen

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

  • ADO im Thread benutzen

    Ich habe ein Datenbank-Abgleichprogramm geschrieben in dem
    mehrere Thread Seriell sowie Paralel Tabellen von der einen in die andere Datenbank pumpen.( SQL-Server 2000 Enterprise )

    Sporadisch furnktioniert die Methode AdoConnection.GetTablenames nicht mehr.( Object or Provider is not capable of performing requested operation )

    Bei ADOCommand sowie ADOQuery treten sporadisch Exceptions auf, mit der Meldung "Unbekannter Fehler".

    Dieselbe Query im QueryAnalyser funktioniert einwandfrei.

    Ich benutze Delphi 5 Enterprise SP1,2 sowie Windows 2000 Pro/Server
    MDAC 2.6

    Für einen kleinen Hinweis wäre ich dankbar.

  • #2
    Hallo,

    hinter ADO verbergen sich COM-Objekte, somit gelten in einem Thread die COM-Regel für die <i>Apartments</i> und den <i>Marshaler</i>. ADO-Objekte "lieben" STAs (Single-threaded Apartments), daher muss der eigene Thread ein Apartment anmelden und am Ende wieder abmelden. Das könnte in einem Beispiel so aussehen:
    <pre>
    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;
    </pre>
    Da ADO (genauer gesagt OLE DB) einen Datenbankverbindung-Pool automatisch verwaltet, muss jeder Thread eine eigene ADO-Connection anfordern. Keine ADO-Objektinstanzen dürfen threadübergreifend verwendet werden.

    Wenn ADO Express-Komponenten im Thread verwendet werden, ist es am sichersten, direkt im Thread eine neue Instanz des Datenmoduls zu erzeugen und dafür eine lokale Variable in Execute zu nutzen. In diesem Fall kommt der Thread niemals mit dem primären Thread der VCL in Konflikt.
    &#10

    Comment


    • #3
      Ich habe jetzt statt in der Application in jedem Data-Pump-Thread im Create CoInitialize und im Destroy CoUninitialize aufgerufen. Habe aber immer noch die selben Probleme.
      Jerder Thread hat auch seine eigene SQL-Verbindung aufgebaut.

      Sporadisch tritt bei ADOQuery.ExecSql der "Unbekannter Fehler" auf.
      Rufe ich gleich danach nochmal ADOQuery.ExecSql auf so tritt der Fehler dann nicht mehr auf.

      Und auch ADOConnection.GetTableNames schlägt sporadisch fehl.

      Vielleicht ist es noch ein anderes Problem ?!?

      Für weitere Anregungen währe ich dankbar..

      Comment


      • #4
        Hallo,

        nutzen die Threads für die Datenbankverbindung einen <i>Firehouse</i>-Cursor (UseServer, ReadOnly, ForwardOnly, CacheSize=1)? Wenn ja, dann ist auf dieser Leitung immer nur eine Aktion gleichzeitig erlaubt.
        Wird nach jeder Einzeloperation die Errors-Kollektion von TADOConnection geleert

        Comment


        • #5
          Die AdoConnection :

          ConnectionTimeout := 10;
          CursorLocation := clUseClient;
          LoginPrompt := False;
          Mode := cmUnknown;

          Der Rest ist Default

          Was genau ist ein Firehouse-Cursor

          Comment


          • #6
            Bei "Unbekannter Fehler" stehlt in der Error-Collection nur SQLState = '40001' und der Provider, aber nicht die Fehlermeldung des Providers

            Comment


            • #7
              Hallo,

              beim Microsoft SQL Server 2000 steht der SQLSTATE-Wert 40001 für die Fehlermeldung "<i>Transaction rollback due to deadlock. </i>". Das Problem hat also nichts mit ADO oder Delphi zu tun, sondern ist ein schlichter Transaktionskonflikt in der Datenbank.

              P.S: Ein Deadlock kann auch dann auftreten, wenn nur eine einzige Tabelle angefasst wird, solange dort sowohl ein clustered als auch ein nonclustered Index verwendet wird und alle abgefragten Spalten nicht komplett über den Index ermittelt werden können

              Comment

              Working...
              X