Announcement

Collapse
No announcement yet.

Three Tier, ApplyUpdates

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

  • Three Tier, ApplyUpdates

    Hallo,

    ich stelle gerade eine ADS-Datenbank auf Oracle, und in diesem Zusammenhang eine Client/Server Anwendung auf Multi Tier (Three-Tier) um
    Folgendes Problem: Am Client wird mit einem TClientDataSet eine einfache Query (Select * From Produkte) an den Server gesendet, welcher die Daten zurück liefert, sie werden ordnungsgemäß im DBGrid dargestellt. Nun möchte ich im Grid Änderungen an den Daten durchführen, und mit ApplyUpdates an den Server zurücksenden.
    Das bleibt ohne Wirkung.
    Die Methode OnReconcileError/HandleReconcileError (ClientDataset) wirft einen Fehler aus, der nur durch zwei Sonderzeichen bezeichnet ist.

    Umfeld Server: TADOConnection über OraOLEDB auf Oracle 9i, ADOQuery, TDatasetProvider, DLL

    Umfeld Client: TDCOMConnection, TClientDataSet, TDataSource, TDBGrid

    IDE: Delphi 6 Enterprise.

    1. hat jemand eine Idee, woran das liegen kann?

    2. Ist es sinnvoller, eine andere Komponente statt der ADOQuery zu verwenden, z.B. TADODataSet? Allerdings wird die Query noch ein klein wenig "komplexer".

    3. Ich habe hier im Forum von TBetterAdoSet gelesen, was kann sie "besser" und wo gibt es sie?

    Für jede Anregung bin ich dankbar, da ich weder Erfahrung mit Oracle noch Multi Tier habe.

    Vielen Dank
    Rüdiger

  • #2
    Hallo,

    eine mit Delphi entwickelte Three-Tier-Datenbankanwendung muss nicht zwangsläuft auf MIDAS (alias DataSnap) hinauslaufen. In Delphi 8 werden zum MIDAS-Server gar nicht mehr unterstützt.

    Wenn auf der Server-Seite der Datenbank-Zugriff über ADO läuft, liegt die Ergebnismenge bereits als gefüllte <b>Recordset</b>-Objektinstanz vor. Wenn auf dem Server und dem Client-Rechner ADO (MDAC) installiert ist, kann das Betriebssystem den Zustand dieses gefüllten Recordset-Objekts vom Server zum Client transportieren. Somit ist an dieser Stelle MIDAS und TClientDataSet technisch betrachtet "völlig überflüssig", da ADO bereits den getrennten Modus über die Bordmittel vollständig unterstützt. Der Rückgriff auf MIDAS macht nur dann Sinn, wenn man <b>kein</b> ADO für den Datenbankzugriff nutzen kann (oder darf).

    Wenn die vom Client an der Datenmenge vorgenommenen Änderungen in die Datenbank zurückgespielt werden sollen, muss der Client die "lebende" Recordset-Objektinstanz zurück zum Server schicken (auf Wunsch werden dabei nur die tatsächlich geänderten Datensätze übertragen). Der Server verbindet dann diese Recordset-Objektinstanz mit einer aktiven Datenbankverbindung (Connection bzw. TADOConnection, um dann alle Änderungen über den Aufruf der Recordset-Methode <b>UpdateBatch</b> einarbeiten zu lassen (d.h. das Recordset-Objekt generiert automatisch alle die SQL-Anweisungen, die dafür notwendig sind).

    Aber auch bei Rückgriff auf ADO bleibt das Problem der Fehler im verwendeten OLE DB-Provider. Daher würde ich zuerst prüfen, ob sich das Problem mit ADO in einer "normalen" Datenbankanwendung reproduzieren lässt (also ohne Three-Tier). Wenn es in einer "normalen" Anwendung läuft, läuft es dann auch in der Three-Tier-Anwendung

    Comment


    • #3
      Hallo Herr Kosch,

      Vielen Dank für Ihre Hinweise.
      Ich habe nun den Zugriff ohne Three-Tier probiert, er funktioniert ( TADOConnection über OraOLEDB.Oracle.1 auf Oracle, TADODataSet, TAdoSet.UpdateBatch)

      Beim Zugriff über 3-Tier verwende ich folgende Komponenten:

      Client: TDCOMConnection, TClientDataSet, über ClientDataSet.ApplyUpdates(-1) soll das Update veranlasst werden.

      Server TADOConnection, TADODataSet, TDataSetProvider.
      Zur Lieferung der Daten vom Server zum Client wird ein SQL-String übergeben.

      Der Versuch, ein ApplyUpdates(-1) in der Methode DataSetProvider.OnUpdate durchzuführen (analog dem Client) führt zu der Fehlermeldung „Missing Dataprovider or Datapacket“. Auch über UpdateBatch kam eine Fehlermeldung (...Closed Dataset). Ich habe keine Idee, wie ich die Daten in der 3-Tier-Umgebung updaten kann.
      Anderseits weiß ich auch nicht, wie ich auf den DataSetProvider und das ClientDataSet verzichten kann, weil ich dann gar keine Verbindung zwischen Server und Client sehe.

      Können Sie mir dabei nicht auf die Sprünge helfen, vielleicht auch mit einem Code-Beispiel.

      Vielen Dank und viele Grüße
      Rüdiger Schwed

      Comment


      • #4
        Hallo,

        &gt;...vielleicht auch mit einem Code-Beispiel.

        das folgende Beispiel für eine Three-Tier-Datenbankanwendung nutzt COM+ auf der Server-Seite, um ein mit Daten gefülltes Recordset an den Client zurückzuliefern (Interface-Methode <i>GetRS</i>) bzw. dessen Änderungen wieder in der Datenbank einzuarbeiten (Interface-Methode <i>UpdRS</i>).
        <br><br>
        <b>Implementierung im COM+ Objekt</b> (Server)
        <br><br>
        Das COM+-Objekt erzeugt das mit den Datensätzen der Ergebnismenge der SELECT-Abfrage gefüllte Recordset, und liefert dieses über den Interface-Zeiger _Recordset an den Client zurück. Dabei das Recordset diesen Transport "überlebt", muss vorher die Datenbankverbindung zwischen Recordset und Datenbank gekappt werden. In der Gegenrichtung sorgf der Server dafür, dass das vom Client als Interface-Zeiger übergebene Recordset mit den vom Anwender geänderten Daten vor dem Aufruf der Methode <b>UpdateBatch</b> kurzzeitig wieder mit der Datenbank verbunden wird.
        <pre>
        <b>unit</b> UpdBatch_Impl;
        <font color="#003399"><i>{$WARN SYMBOL_PLATFORM OFF}</i></font>
        <br>
        <b>interface</b>
        <br>
        <b>uses</b>
        ActiveX, Mtsobj, Mtx, ComObj, UpdBatch_Impl_TLB, StdVcl, ADODB_TLB;
        <br>
        <b>type</b>
        TUpdBatchObj = <b>class</b>(TMtsAutoObject, IUpdBatchObj)
        <b>protected</b>
        <b>procedure</b> UpdRS(<b>const</b> aRS: _Recordset; out sSrvMsg: WideString); <b>safecall</b>;
        <b>procedure</b> GetRS(out aRS: _Recordset; out sSrvMsg: WideString);
        <b>safecall</b>;
        <font color="#003399"><i>{ Protected-Deklarationen }</i></font>
        <b>end</b>;
        <br>
        <b>implementation</b>
        <br>
        <b>uses</b> ComServ, Variants;
        <br>
        <b>resourcestring</b>
        cCS = <font color="#9933CC">'Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;'</font> +
        <font color="#9933CC">'Initial Catalog=tempdb;Data Source=(local)'</font>;
        <br>
        <b>procedure</b> TUpdBatchObj.GetRS(out aRS: _Recordset; out sSrvMsg: WideString);
        <b>var</b>
        aCon : _Connection;
        aRSTmp: _Recordset;
        <b>begin</b>
        aCon := CoConnection.Create <b>as</b> _Connection;
        aCon.CursorLocation := adUseClient;
        aCon.Open(cCS, <font color="#9933CC">''</font>, <font color="#9933CC">''</font>, adConnectUnspecified);
        <b>try</b>
        aRSTmp := CoRecordSet.Create <b>as</b> _Recordset;
        aRSTmp.CursorLocation := adUseClient;
        aRSTmp.Open(<font color="#9933CC">'select PK, Wert1, Wert2 from UpdateBatchNOTNULL'</font>, aCon,
        adOpenStatic, adLockBatchOptimistic, adCmdText);
        aRS := aRSTmp;
        aRS._Set_ActiveConnection(<b>nil</b>);
        <b>finally</b>
        aCon.Close;
        <b>end</b>;
        sSrvMsg := <font color="#9933CC">'ok'</font>;
        SetComplete;
        <b>end</b>;
        <br>
        <b>procedure</b> TUpdBatchObj.UpdRS(<b>const</b> aRS: _Recordset;
        out sSrvMsg: WideString);
        <b>var</b>
        aCon : _Connection;
        <b>begin</b>
        aCon := CoConnection.Create <b>as</b> _Connection;
        aCon.CursorLocation := adUseClient;
        aCon.Open(cCS, <font color="#9933CC">''</font>, <font color="#9933CC">''</font>, adConnectUnspecified);
        aRS.Set_ActiveConnection(aCon);
        aRS.UpdateBatch(adAffectAll);
        aRS._Set_ActiveConnection(<b>nil</b>);
        aCon.Close;
        aCon := <b>nil</b>;
        sSrvMsg := <font color="#9933CC">'ok'</font>;
        SetComplete;
        <b>end</b>;
        <br>
        <b>initialization</b>
        TAutoObjectFactory.Create(ComServer, TUpdBatchObj, Class_UpdBatchObj,
        ciMultiInstance, tmApartment);
        <b>end</b>.
        </pre>
        <b>Implementierung im Client</b><br><br>
        Der Client hat keine Verbindung zur Datenbank, er kennt noch nicht einmal die Datenbank, von der die Daten stammen. Statt dessen erhält der Client vom COM+-Objekt des Servers nur das bereits mit den Datensätzen der Ergebnismenge gefüllte Recordset, um dieses einer <b>TADODataSet</b>-Komponente unterzuschieben. Diese TADODataSet-Komponente wird im Objektinspektor <b>nicht</b> konfiguriert, sondern die Komponeten übernimmt die Daten direkt vom bereits gefüllten Recordset-Objekt. TADODataSet dient nur dazu, über TDataSource ein TDBGrid zur Bearbeitung der Datenmenge nutzen zu können.
        <pre>
        <b>unit</b> CltUpdateBatchFrm;
        <br>
        <b>interface</b>
        <br>
        <b>uses</b>
        Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
        Dialogs, DB, ADODB, ExtCtrls, DBCtrls, Grids, DBGrids, StdCtrls,
        UpdBatch_Impl_TLB, ComCtrls;
        <br>
        <b>type</b>
        TForm1 = <b>class</b>(TForm)
        Button1: TButton;
        DBGrid1: TDBGrid;
        DBNavigator1: TDBNavigator;
        ADODataSet1: TADODataSet;
        DataSource1: TDataSource;
        StatusBar1: TStatusBar;
        Button2: TButton;
        <b>procedure</b> FormCreate(Sender: TObject);
        <b>procedure</b> Button1Click(Sender: TObject);
        <b>procedure</b> Button2Click(Sender: TObject);
        <b>private</b>
        <font color="#003399"><i>{ Private-Deklarationen }</i></font>
        FSrv : IUpdBatchObj;
        <b>public</b>
        <font color="#003399"><i>{ Public-Deklarationen }</i></font>
        <b>end</b>;
        <br>
        <b>var</b>
        Form1: TForm1;
        <br>
        <b>implementation</b>
        <br>
        <font color="#003399"><i>{$R *.dfm}</i></font>
        <br>
        <b>uses</b> ADODB_TLB, ADOInt;
        <br>
        <b>procedure</b> TForm1.FormCreate(Sender: TObject);
        <b>begin</b>
        FSrv := CoUpdBatchObj.CreateRemote(<font color="#9933CC">'192.168.10.1'</font>);
        <b>end</b>;
        <br>
        <b>procedure</b> TForm1.Button1Click(Sender: TObject);
        <b>var</b>
        aRS : ADODB_TLB._Recordset;
        swSrvMsg : WideString;
        <b>begin</b>
        FSrv.GetRS(aRS, swSrvMsg);
        ADODataSet1.Recordset := ADOInt._Recordset(aRS);
        ADODataSet1.Active := True;
        StatusBar1.SimpleText := swSrvMsg;
        <b>end</b>;
        <br>
        <b>procedure</b> TForm1.Button2Click(Sender: TObject);
        <b>var</b>
        aRS : ADODB_TLB._Recordset;
        swSrvMsg : WideString;
        <b>begin</b>
        aRS := ADODB_TLB._Recordset(ADODataSet1.Recordset);
        FSrv.UpdRS(aRs, swSrvMsg);
        StatusBar1.SimpleText := swSrvMsg;
        <b>end</b>;
        <br>
        <b>procedure</b> TForm1.ADODataSet1NewRecord(DataSet: TDataSet);
        <b>begin</b>
        ADODataSet1.FieldByName(<font color="#9933CC">'Wert2'</font>).Value := <font color="#9933CC">''</font>;
        <b>end</b>;
        <br>
        <b>end</b>.
        </pre>
        Die ganzen Hintergründe dazu sind in den folgenden Büchern zu finden: <br>
        a) http://www.software-support.biz/sus/sus_buch/psecom,id,4,nodeid,11,_language,de.html<br>
        b) http://www.software-support.biz/sus/sus_buch/psecom,id,21,nodeid,11,_language,de.htm

        Comment


        • #5
          Hallo,

          das Thema würde auch mich brennend interessieren, da ich viel mit ADO arbeite und jetzt eine three-tier-Anwendung schreiben will. Es wäre schön, wenn Du den link zu den nicht mehr existierenden Büchern in Klartext umsetzen könntest...

          Grüße

          Juli

          Comment

          Working...
          X