Announcement

Collapse
No announcement yet.

Updatebatch - Daten über RDS ändern

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

  • Updatebatch - Daten über RDS ändern

    Hallo,<br>
    ich habe gerade versucht das Beispiel aus <b>ADO und Delphi</b> von A.Kosch
    auszuprobieren, nachdem ich mit meinem eigenen Programm nicht weitergekommen bin. Dort tritt aber das gleiche Problem auf. Folgende Umgebung:<br>
    <ul>
    <li>Delphi 6 UP2</li>
    <li>MS-SQL Server 7</li>
    <li>Tabelle mit einem Autoinkrement Feld als Primary Key und einem weiteren Stringfeld</li>
    <li>TADORecordset mit clUseClient und ltBatchOptimistic
    <br>
    Das Problem:<br>
    Nach dem <b>Löschen</b> eines Datensatzes mit anschliessendem UpdateBatch(adAffectAll) bekomme ich die Fehlermeldung:
    <b>Die zum aktualisieren angegebene Zeile wurde nicht gefunden. Einige Werte wurden seit dem letzten lesen ggf geändert.</b><br>
    Sowohl mit ADO-Connection als auch mit RDS (das Beispiel aus dem Buch Kapitel 11.5.3) bekomme ich die gleiche Meldung.<br>
    Verwende ich eine Tabelle ohne Autoinkrement Feld, klappt alles.<br>
    Vielen Dank und Grüsse<br>
    Hermann

  • #2
    Hallo,

    ich kann das Problem nicht reproduzieren (Windows XP Professional, ADO 2.7, MS SQL Server 2000 SP2), daher würde ich zuerst prüfen, ob das folgende Testprogramm so funktioniert wie erwartet:

    Tabelle in der MS SQL Server-Datenbank:
    <pre>
    CREATE TABLE SchmidtProblem (
    RecID INTEGER NOT NULL IDENTITY PRIMARY KEY,
    Feld VARCHAR(10))
    GO
    INSERT INTO SchmidtProblem (Feld) VALUES ('Test')
    GO
    </pre>
    Änderung im Beispielprojekt ClientRDSUpdateBatch.dpr (von der CDROM zum Buch):
    <pre>
    uses ADODB_TLB, ADOInt, RDSUpdateBatch_TLB;

    const
    sSrvIP = '192.168.10.1';
    sSQL = 'SELECT * FROM SchmidtProblem';

    procedure TForm1.ButtonDownloadClick(Sender: TObject);
    var
    vRS : OleVariant;
    aSrv : IRDSUpdateBatchObj;
    aRS : _RecordSet;
    swSrvMsg : WideString;
    begin
    //vRS := RDSConnection1.AppServer.DownloadRS(EditSQL.Text, swSrvMsg);
    //aRS := IUnknown(vRS) as _RecordSet;
    aSrv := CoRDSUpdateBatchObj.CreateRemote(sSrvIP);
    // SELECT * FROM SchmidtProblem
    aRS := ADOInt._Recordset(aSrv.DownloadRS(sSQL, swSrvMsg));
    ADODataSetResult.RecordSet := aRS;
    ADODataSetResult.Active := True;
    StatusBar1.SimpleText := swSrvMsg;
    end;

    procedure TForm1.ButtonUploadClick(Sender: TObject);
    var
    aSrv : IRDSUpdateBatchObj;
    swSrvMsg : WideString;
    begin
    //RDSConnection1.AppServer.UploadRS(ADODataSetResult .Recordset,swSrvMsg);
    aSrv := CoRDSUpdateBatchObj.CreateRemote(sSrvIP);
    aSrv.UploadRS(ADODB_TLB.Recordset(ADODataSetResult .Recordset), swSrvMsg);
    StatusBar1.SimpleText := swSrvMsg;
    end;
    </pre>
    Wenn dann immer noch nicht der Datensatz erfolgreich gelöscht werden kann, könnte man auch den Server-Teil im Projekt RDSUpdateBatch.dpr ändern. Mit der Zeile <b>aRS.Properties['Update Criteria'].Value := adCriteriaKey;</b> wird ADO beauftragt, für die DELETE- bzw. UPDATE-Anweisungen nur den Primärschlüsselwert des betroffenen Datensatzes als WHERE-Kriterium zu verwenden:
    <pre>
    procedure TRDSUpdateBatchObj.UploadRS(const aRS: _Recordset;
    out sSrvMsg: WideString);
    var
    aConnection : _Connection;
    aUpdateRS : _Recordset;
    swSQL : WideString;
    begin
    aConnection := CoConnection.Create;
    aConnection.CursorLocation := adUseClient;
    aConnection.Open(cCS, '', '', adConnectUnspecified);
    aUpdateRS := CoRecordset.Create;
    try
    aUpdateRS.Open(aRS, aConnection, adOpenUnspecified,
    adLockUnspecified, adCmdUnspecified);
    aRS.Properties['Update Criteria'].Value := adCriteriaKey;
    aRS.UpdateBatch(adAffectAll);
    finally
    aConnection.Close;
    end;
    end;
    </pre>
    P.S: Ist in der Datenbank ein Trigger aktiv, der weitere Datensätze ändert/löscht

    Comment


    • #3
      Vielen Dank für die schnelle Antwort,<br>
      zu meinem Bedauern muss ich hinzufügen, dass ich vergessen hatte, dass in der Tat ein Delete-Trigger zum Einsatz kommt, der so aussieht:<br>
      <pre>
      CREATE TRIGGER [TR_CUSTOMERS_DELETE] ON dbo.Customers
      FOR DELETE
      AS
      Delete Orders from Deleted where Orders.Customers_ID = deleted.Customers_ID
      </pre>
      Damit ist mir immer noch nicht ganz klar, warum diese Fehlermeldung erscheint, da eine ganz andere Tabelle betroffen ist.
      <p>&nbsp;</p>
      Ich habe aber in diesem Zusammenhang noch eine andere Frage.<br>
      Ich versuche über eine eigene DataFactory native Recordsets an den Server zu übertragen und dafür die Methode UpdateBatch() des TADODataSet zu überschreiben, damit von da aus die eigene Datafactory
      aufgerufen werden kann. Das klappt soweit auch ganz gut, nur muss immer das gesamte auf Clientseite vorhandene Recordset übertragen werden, auch wenn man UpdataBatch(arCurrent) ausführt oder auch nur
      ein Datensatz geändert wurde, da das eigentliche Updatebatch von der serverseitigen Datafactory ausgeführt wird.<br>
      Jetzt die Frage:<br>
      Auf Clientseite existiert ein Recordset im Batchoptimistic-Modus. Nach einer oder (mehreren) Änderung(en) soll das Recordset (bzw. die Änderungen an dem Recordset) an den Server übertragen werden, der dann das eigentliche Updatebatch durchführen soll. Gibt es eine Möglichkeit, anstelle des ganzen Recordsets, nur die relavanten Daten
      (adRecNew, adRecModified ..) in ein Recordset zu packen und dann dieses zum Server zu schicken ? Man würde sich damit das unnötige Hin- und Herschicken von unveränderten Daten ersparen. Clone kommt meiner Meinung nach nicht in Frage, da die neue Instanz auf den Originaldaten arbeitet. Eine Kopie, die dann sämtliche Informationen des original vom Provider abgerufenen Recordsets enthält, stelle ich mir auch nicht so einfach vor, wenn Sie überhaupt möglich ist. In dieser Kopie (wenn möglich ?) könnte man dann zumindest vor dem Übetragen die ungeänderten Datensätze entfernen.<br>
      Vielleicht gibt es ja irgendwo schon einmal einen solchen Ansatz.<br>
      Vielen Dank und Grüsse<br>
      Hermann Schmid

      Comment


      • #4
        Hallo,

        &gt;..Damit ist mir immer noch nicht ganz klar, warum diese Fehlermeldung erscheint..

        um zu prüfen, ob der UPDATE-/DELETE-Vorgang erfolgreich war, holt sich das Recordset-Objekt vom SQL-Server die Anzahl der betroffenen Datensätze. Wenn ein Trigger mit im Spiel ist, der ebenfalls löscht, kommt alles auf die gleiche Rechnung.

        &gt;..nur die relavanten Daten (adRecNew, adRecModified ..) in ein Recordset zu packen ..

        Selbstverständlich - über die Recordset-Eigenschaft <b>MarshalOptions</b> (Wert <b>moMarshalModifiedOnly</b>) kann man ADO beauftragen, automatisch nur die betroffenen Datensätze zurück zum Server zu schicken. In meinem ADO-Buch gehe ich auf der Seite 361 darauf ein

        Comment


        • #5
          Hallo Herr Kosch,<br>
          vielen Dank. Ich weiss nicht, ob MarshalOptions etwas bringt, da das eigentliche Updatebatch(..), welches in die Datenbank schreibt, auf Serverseite stattfindet. D.h. das (verbindungslose, teilweise geänderte) Recordset muss erstmal vom Client per RDS dahin kommen - und da bleibt mir im Augenblick nur der Weg, das als ganzes zu tun (also auch alle ungeänderten Datensätze). Interessant wäre, ein Recordset auf Clientseite zu bekommen, welches nur die geänderten Datensätze enthält und das dann per RDS an den Server geschickt werden kann.<br>
          Viele Grüsse<br>
          Hermann Schmid

          Comment


          • #6
            Hallo,

            die MarshalOptions werden auf der <b>Client</b>-Seite gesetzt, bevor das Recordset zum Server transportiert wird. Um den Rest kümmert sich das Recordset-Objekt dann automatisch. Im Beispiel aus der Seite 361 wird das nicht sofort deutlich, da sich alle Ebenen in der gleichen Anwendung befinden - ich hätte besser eine andere Seite im Buch als Beispiel heraussuchen sollen :-)

            Bei meiner Antwort zum TRIGGER-Problem habe ich ganz vergessen, darauf hinzuweisen, dass über die Zeile <b>SET NOCOUNT ON</b> (erste T-SQL-Anweisung im Trigger) das Mitzählen der vom Trigger betroffenen Datensätze abgeschaltet werden kann

            Comment


            • #7
              Hallo Herr Kosch,<br>
              ich habe mal auf Serverseite in meiner eigenen Servermethode "Updatebatch" (respektive Submitchanges) mitprotokolliert (Wie gesagt, es
              handelt sich nicht um die Standard RDSServer.DataFactory). Ganz gleich
              wie MarshalOptions (selbstverständlich auf Clientseite) eingestellt ist: auf Serverseite kommen immer alle
              Datensätze an, auch wenn am Recordset auf Clientseite gar nichts geändert wurde.<br>
              Viele Grüsse<br>
              Hermann Schmid

              Comment

              Working...
              X