Announcement

Collapse
No announcement yet.

Kein Rowset bei COM+

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

  • Kein Rowset bei COM+

    Hallo

    Ich habe eine Com-Dll erstellt mit nativen ADO Zugriff auf eine Sql 2000 Datenbank. Als Result lasse ich mir batch-optimistische Recordsets zurückliefern.

    Die Ergebnismenge ist <B> immer </B> beim Client vorhanden, wenn Client und COM-DLL auf dem gleichen System ausgeführt werden. Wird die COM-DLL auf einem Win 2000 Server ausgeführt, erhalte ich <B> manchmal </B>, je nach Zugriffsmethode, die folgenden Meldungen.

    <B> Zugriff verweigert </B> oder
    <B> Das Rowset kann nicht geladen werden, da der Datenstrom ungültig ist </B>

    Client Umgebung:
    Win 200 Pro, Delphi C/S mit Delphi Patch und ADO-Express Patch, MDAC 2.61

    Server Umgebung:
    Win 2000 Server Patch 1, Sql 2000 Patch 1, MDAC 2.61

    Der Client ruft das Objekt wie folgt auf:

    var
    Server : IMetaDaten_Srv;
    aRS1 : ADODB_TLB._Recordset;
    aRS2 : ADOInt._Recordset;
    begin
    FMTSServer := CoTest2_Srv.Create;

    aRS2 := IUnknown( FMTSServer.GetData( ConnStr, SqlStr)) as ADOInt._Recordset;
    ADODataSet1.Recordset := aRS2;
    //oder
    aRS1 := FMTSServer.GetData( ConnStr, SqlStr) as ADODB_TLB._Recordset;
    ADODataSet1.Recordset := ADOInt._Recordset(aRS1);
    end;

    Kann mir jemand helfen ?

    Danke

    O.Wilms

  • #2
    Hallo,

    der Transport eines lebenden Recordsets von einem COM+ Application Server zu einem anderen Rechner ist kein Problem - diese Fehlermeldung habe ich noch niemals zu Gesicht bekommen. Bei mir sieh eine solche Implementierung zum Beispiel so aus (im einfachen Beispiel verwende ich ADO Express, normalerweise taucht hier nur das native Connection-/Recordset-Objekt auf):

    COM+ Objekt:
    <pre>
    function TFXDBObjReadOnly.GetRS(const sName: WideString): _Recordset;
    begin
    try
    try
    ADOConnection1.Connected := True;
    ADODataSet1.Active := True;
    Result := ADODB_TLB._Recordset(ADODataSet1.Recordset);
    ADODataSet1.Connection := nil;
    ADODataSet1.Close;
    SetComplete;
    finally
    ADOConnection1.Connected := False;
    end;
    except
    SetAbort;
    raise;
    end;
    end;
    </pre>
    Client auf dem externen Rechner:
    <pre>
    uses ADOInt;

    procedure TForm1.Button1Click(Sender: TObject);
    var
    aSrv : IFXDBObjReadOnly;
    aRS : Recordset15;
    swName : WideString;
    begin
    aSrv := CoFXDBObjReadOnly.Create;
    aRS := ADOInt._Recordset(aSrv.GetRS(swName));
    ADODataSet1.Recordset := _Recordset(aRS);
    ADODataSet1.Active := True;
    end;
    </pre>
    Ich vermute einmal, dass die Implementierung/Konfiguration des COM+ Objekts die Ursache für dieses Problem ist. Alle Delphi-Versionen sind an dieser Stelle ja nach verwendeter VCL-Klasse nicht absolut threadfest, so dass man bei Dual-Prozessor-Servern von Hand eingreifen muss, wenn "moderne" COM+ Sachen genutzt werden (TNA, Objekt-Pool etc.).

    P.S: Je nach verwendeten Delphi-Version (inkl. Patch) muss man die Interface-Typen für Recordset anpassen (da jede Version beim Import der Typbibliothek einen anderen Output generiert). Delphi 6 wollte zum Beispiel am Anfang immer Recordset15 haben

    Comment


    • #3
      Hallo Herr Kosch,

      erst einmal vielen Dank für Ihre hilfe, leider habe ich das Problem nach zahlreichen Tests immer noch.

      Getestet habe ich verschiedene Konfigurationen mit Win2000, NT4.0, MTS 2.0, COM+, VB6.0 als Client und COM-Objekt Erstellung und Delphi 5.0 Ent als Client und COM-Objekt Erstellung.

      Das Problem scheint nur aufzutreten, wenn sehr viele Datensätze abgerufen werden ( ca. 50000). Ich weiß das man im Normalfall nicht so viele Datensätze auf den Client laden sollte, hat aber seinen Grund und sollte ja auch funktionieren.

      Im Profiler des Sql-Servers werden alle Aufrufe ( auch mit mehreren Clients gleichzeitig) als BatchComplete ohne Error abgeschlossen, die Methode "EventProtCom" im COM+ Objekt protokolliert die Anzahl der Datensätze im EventLog und auch dort wird immer die korrekte Anzahl an Datensätzen ausgegeben auch wenn im Anschluß der Client eine Fehlermeldung ausgibt.

      Außerdem habe festgestellt das sich die Fehlermeldungen unterscheiden je nachdem wie das COM+ Objekt im MTS konfiguriert ist.

      Konfiguration:
      Authentifizierungsebene für Aufrufer = Paket
      Fehlermeldung = Zugriff verweigert
      Authentifizierungsebene für Aufrufer = Verbinden
      Fehlermeldung = Das Rowset kann nicht geladen werden, da der Datenstrom ungültig ist

      Vieleicht haben Sie noch eine Idee, mein Latain ist am Ende.

      COM+ Objekt auf Server:
      <PRE>
      function TTest2_Srv.GetData( const Connstr, SqlStr : WideString) : _Recordset;
      var
      ActConnection : _Connection;
      ActRecordset : _Recordset;

      begin

      ActConnection := CoConnection.Create;
      ActRecordset := CoRecordset.Create;

      ActConnection.CursorLocation := adUseClient;
      ActConnection.Open( ConnStr, '', '', 0);

      try
      try
      ActRecordset.Open( SqlStr, ActConnection, adOpenStatic, adLockBatchOptimistic, adCmdText);
      except
      raise EOleSysError.Create( 'Konte Recordset nicht lesen', -1, 0);
      end;

      finally
      ActRecordset.Set_ActiveConnection( nil);
      ActConnection := nil;

      Result := ActRecordset;

      EventProtCOM( ' Anzahl: ' + IntToStr(ActRecordset.Get_RecordCount) );
      ObjectContext.SetComplete;
      end;
      end;

      Client auf externem Rechner:

      procedure TForm1.Button1Click(Sender: TObject);
      var

      aRS1 : ADOInt.Recordset15;
      ConnStr : WideString;
      SqlStr : WideString;

      begin
      FMTSServer := CoTest2_Srv.Create;

      ADODataSet1.Active := false;

      ConnStr := 'Provider=SQLOLEDB.1;Persist Security Info=False;' +
      'User ID=TestUser;Password=test;Initial Catalog=TestAnwendung;Data Source=TestSERVER';
      SqlStr := 'Select * from Kunden');

      aRS1 := ADOInt._Recordset(FMTSServer.GetData(ConnStr, SqlStr));

      ADODataSet1.Recordset := _Recordset(aRS1);
      ADODataSet1.Active := true;

      end;
      </PRE>

      Vielen Dank im Voraus

      O.Wilm

      Comment


      • #4
        Hallo,

        wenn das Problem nur bei sehr umfangreichen Datenmengen (= langen Ladezeiten) auftritt, kann eventuell der <b>Transaktions-Timeout</b>-Wert des COM+ Objekts (siehe Registerseite <i>Transaktionen</i>) zu kurz sein.

        &gt;Im Profiler des Sql-Servers werden alle Aufrufe ( auch mit mehreren Clients gleichzeitig) als BatchComplete ohne Error <br>
        &gt;abgeschlossen, die Methode "EventProtCom" im COM+ Objekt protokolliert die Anzahl der Datensätze im EventLog und auch dort <br>
        &gt;wird immer die korrekte Anzahl an Datensätzen ausgegeben auch wenn im Anschluß der Client eine Fehlermeldung ausgibt.

        Das ist normal, den bei <b>clUseClient</b> holt sich ADO beim Öffnen des Recordset-Objekts über den sogenannten Firehouse Cursor sofort alle Datensätze der Ergebnismenge in einem Stück - der Transport zum Client erfolgt erst später im zweiten Schritt (für den SQL Server ist das COM+ Objekt der Client). Wenn der Transport vom COM+ Objekt zum Base Client später schiefgeht (Timeout?), ist der SQL Server zu diesem Zeitpunkt bereits unbeteiligt.
        &#10

        Comment

        Working...
        X