Announcement

Collapse
No announcement yet.

Table/DataSet --> TClientDataSet, vice versa

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

  • Table/DataSet --> TClientDataSet, vice versa

    Hallo,

    ich möchte den Inhalt bestimmter Tabellen für Testzwecke auf Festplatte speichern und bei Bedarf wieder in die Tabelle zurückladen können.

    Das Speichern auf Festplatte ist OK.<br>
    Das Laden von Platte ist nicht möglich.

    // TempClientDataSet im DatenModul deklariert

    procedure Speichern;<br>
    var TempProvider:TDataSetProvider;<br>
    begin<br>
    TempProvider := TDataSetProvider.Create(nil);

    // 1. Aus der Original-Datenmenge dem Provider zuweisen<br>
    TempProvider.DataSet := SourceDataSet;

    // 2. Aus dem Provider der Client-Datenmenge zuweisen<br>
    TempClientDataSet1.Data := TempProvider.Data;

    // 3. Aus der Client-Datenmenge auf Festplatte speichern<br>
    TempClientDataSet1.FileName := 'C:\TCL'; // Filename zuweisen<br>
    TempClientDataSet1.SaveToFile;

    FreeAndNil(TempProvider);<br>
    end;

    Procedure Laden;<br>
    var TempProvider:TDataSetProvider;<br>
    begin<br>
    TempProvider := TDataSetProvider.Create(nil);

    // 1. Von der Festplatte in die Client-Datenmenge laden<br>
    TempClientDataSet1.FileName := 'C:\TCL';<br>
    TempClientDataSet1.LoadFromFile;

    // 2. Aus der Client-Datenmenge dem Provider zuweisen<br>
    TempProvider.DataSet := TempClientDataSet1; // <b>produziert Fehler</b><br>

    TempProvider.Data := TempClientDataSet1.Data; // <b> Fehler, weil nurLeseMenge</b><br>

    // 3. Aus dem Provider der Original-Datenmenge zuweisen<br>
    SourceDataSet := TempProvider.DataSet;

    FreeAndNil(TempProvider);<br>
    end;

    <b>Ich habe keine Lösung für <b>Punkt 2</b><br>
    Wer weiß Rat ?

    Gruss<br>Helmut

  • #2
    Hallo,

    das folgende Beispiel stammt aus einem Artikel von mir, der im Jahr 1999 in DER ENTWICKLER erschienen ist. Der Benutzer kann die vom DCOM-Server abgeforderten Daten in eine Daten zwischenspeichern, die Verbindung zum Server trennen, die Daten editieren und am Ende das Ganze wieder zum Server schicken:
    <pre>
    unit BriefcaseDBCltFrm;

    interface

    uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    ComCtrls, DBClient, MConnect, StdCtrls, Buttons, ExtCtrls, Grids,
    DBGrids, DBCtrls, Db;

    type
    TFormClient = class(TForm)
    StatusBar1: TStatusBar;
    Panel1: TPanel;
    SBtnStart: TSpeedButton;
    DCOMDBSrv: TDCOMConnection;
    DBNavigator1: TDBNavigator;
    DBGrid1: TDBGrid;
    RadioGroupConnect: TRadioGroup;
    EditUserName: TEdit;
    EditDBFile: TEdit;
    ClientDataSetBC: TClientDataSet;
    DataSource1: TDataSource;
    SBtnUpdate: TSpeedButton;
    procedure RadioGroupConnectClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure SBtnUpdateClick(Sender: TObject);
    procedure ClientDataSetBCReconcileError(DataSet: TClientDataSet;
    E: EReconcileError; UpdateKind: TUpdateKind;
    var Action: TReconcileAction);
    procedure EditUserNameExit(Sender: TObject);
    private
    { Private-Deklarationen}
    FCltID : Integer;
    public
    { Public-Deklarationen}
    end;

    var
    FormClient: TFormClient;

    implementation

    {$R *.DFM}

    uses RecError;

    procedure TFormClient.FormCreate(Sender: TObject);
    begin
    FCltID := GetCurrentThreadID;
    EditDBFile.Text := Format('C:\Database\%s.dat',[EditUserName.Text]);
    end;

    procedure TFormClient.RadioGroupConnectClick(Sender: TObject);
    begin
    case RadioGroupConnect.ItemIndex of
    0 : begin
    ClientDataSetBC.Active := False;
    DCOMDBSrv.Connected := False;
    SBtnUpdate.Enabled := False;
    end;
    1 : begin
    ClientDataSetBC.FileName := EditDBFile.Text;
    DCOMDBSrv.Connected := True;
    DCOMDBSrv.AppServer.AddUser(FCltID, WideString(EditUserName.Text));
    ClientDataSetBC.Active := True;
    SBtnUpdate.Enabled := True;
    end;
    2 : begin
    DCOMDBSrv.Connected := False;
    ClientDataSetBC.FileName := EditDBFile.Text;
    ClientDataSetBC.Active := True;
    SBtnUpdate.Enabled := False;
    end;
    end;
    end;

    procedure TFormClient.SBtnUpdateClick(Sender: TObject);
    begin
    ClientDataSetBC.ApplyUpdates(-1);
    end;

    procedure TFormClient.ClientDataSetBCReconcileError(
    DataSet: TClientDataSet; E: EReconcileError; UpdateKind: TUpdateKind;
    var Action: TReconcileAction);
    begin
    Action := HandleReconcileError(DataSet, UpdateKind, E);
    end;

    procedure TFormClient.EditUserNameExit(Sender: TObject);
    begin
    EditDBFile.Text := Format('C:\Database\%s.dat',[EditUserName.Text]);
    end;

    end.
    </pre&gt

    Comment


    • #3
      Danke für Ihre Info.

      Soweit ich ersehen kann, ist für das Rückschreiben aus dem TClient-Set in die Tabelle der Datenbank ein DCOM-Provider erforderlich.

      So einfach das Auslesen der Tabelle ist (Übergabe der Tabelle zum TClient-Dataset), so kompliziert erscheint das Rückschreiben.

      Ich bin bisher an der Erstellung des Providers gescheitert. Gibt es keine einfachere Lösung ? Die Daten des Client-Datasets brauchen nicht eingemischt werden, sondern ersetzen die komplette Tabelle (diese wird vorher geleert)

      Comment


      • #4
        Hallo,

        in diesem Fall ist es doch am einfachsten, alle Datensätze des ClientDatasets in einer Schleife durchzugehen, alle Daten auszulesen und "von Hand" als neuen Datensatz in die Zieltabelle einzufügen. <br>
        Mann kann allerdings den Provider in die gleiche Anwendung einfügen, eine Aufteilung auf verschiedene Anwendung ist nicht notwendig. Wenn alles in einer EXE ist, fallen sogar keine MIDAS-Lizenzkosten an

        Comment


        • #5
          Hallo,

          Wenn ich die Datensätze des ClientDatasets in einer Schleife durchgehe und in eine TTable-Tabelle übergeben möchte, muss ich doch jedes Feld einzeln vor dem Insert zuweisen ?

          Bei größeren Tabellen eine Unmenge Arbeit. Erschwerend ist noch, dass es sich um mehr als 60 verschiedene Tabellen handelt.

          Ich habe eine "neutrale" Prozedur welche die Tabelle übergeben bekommt und über den Umweg ClientDataSet auf die Festplatte schreibt.

          Bei Bedarf möchte ich die Tabelle der Datenbank komplett mit dem Inhalt des ClientDataSets überschreiben (auf den Ausgangszustand restaurieren).

          Ich arbeite an einem sehr komplexen Programm. Die Tabellen sind alle miteinander mehrfach verknüpft. Ich habe diese aus bestimmten Gründen nicht über die referenzielle Integrität miteinander verbunden, sondern erledige dies in meinem Programm selber.

          Ich hätte sonst grosse Probleme, bei einer allfälligen Spaltenänderung oder Einfügung eine Tabelle vorübergehend zu löschen.

          Da ich für Test- und Kontrollzwecke sehr oft die Daten der einzelnen Tabellen manipulieren muss, speichere ich diese vorab auf Festplatte und möchte sie nachher einfach wieder auf den Ausgangszustand zurücksetzen können.

          Im anderweitigen Fall müsste ich jeweils ein Restore der kompletten Datenbank durchführen, welches ungleich zeitaufwendiger scheint.

          Daher benötige ich die Funktion der Zurückschreibung in die Datenbank.

          Ich habe bisher noch nie mit Providern gearbeitet und bin bei der Definition eines solchen bisher nicht klar gekommen.

          Gruss<br>Helmu

          Comment


          • #6
            Hi Helmut,
            <br>
            <br>wie groß ist die Datenbank? 100 MB? Die Frage ist ob es nicht doch sinnvoller ist "kurz" ein Backup der Daten zu machen. Denn wenn du die Daten der original Tabelle mit einem Backup überschreiben willst wirst du sehr wahrscheinlich nicht um ein Drop Table herumkommen (löschen von vielen Datensätzen bekommen die meisten Filebasierten DB'S nicht hin (zumindest nicht schnell)) und dann mußt du "...jedes Feld einzeln..." wieder in der Tabelle erstellen. "Bei größeren Tabellen eine Unmenge Arbeit."
            <br>
            <br>Aber du kannst auch über die Fields Eigenschaft eines Datasets alle Felder einer Datenmenge durchlaufen (automatisch) und den Inhalt in eine zweite Datenbank schreibe, wie A.Kosch schon sagte.
            <br>
            <br>mfg
            <br>p

            Comment


            • #7
              Frage:

              Es existiert ein TClientDataSet und eine TTable.

              Wie kann ich per Fields die Daten vom ClientDataSet in die Tabelle bekommen ?

              for x := 0 to ClientDataset.Fields.Count -1 do<br>
              Table.Database.Datasets[x] := ClientDataSet.Fields[x].Value;

              DataSets ist eine NurLesen-Eigenschaft

              Comment


              • #8
                ... Aber du kannst auch über die Fields Eigenschaft eines Datasets alle Felder einer Datenmenge durchlaufen (automatisch) und den Inhalt in eine zweite Datenbank schreibe, wie A.Kosch schon sagte. ...

                Wie soll das gehen ?

                Gruss<br>Helmu

                Comment


                • #9
                  Hallo,

                  angenommen, auf dem lokalen Rechner soll der Inhalt einer Lookup-Tabelle in Form einer Datei zwischengespeichert und beim Programmstart geladen werden. In diesem Fall könnte man folgendes machen, wobei das Programm immer dann die Daten neu vom Server anfordert, wenn die Datei nicht lokal vorgefunden wird:
                  <pre>
                  resourcestring
                  cCDSName = 'CDSLOOK.CDS';

                  procedure TFormMain.FormCreate(Sender: TObject);
                  begin
                  FCSDFileName := Format('%s%s', [ExtractFilePath(Application.ExeName), cCDSName]);
                  StatusBar1.SimpleText := FCSDFileName;
                  if not FileExists(FCSDFileName) then
                  begin
                  QueryLoadJob.Active := True;
                  ClientDataSetJob.Data := DataSetProviderJob.Data;
                  QueryLoadJob.Active := False;
                  ClientDataSetJob.SaveToFile(FCSDFileName);
                  end
                  else
                  ClientDataSetJob.LoadFromFile(FCSDFileName);
                  TableEMPLOYEE.Active := True;
                  end;
                  </pre>
                  Nach dem Daten der Daten ist die TClientDataSet-Instanz eine ganz normale Datenmenge wie alle anderen auch, so dass über <b>First</b> und <b>Next</b> jeder Datensatz einzeln durchblättert/ausgewertet und in die Zieltabelle eingefügt (SQL oder AppendRecord) werden kann. Über <b>Filter</b> und <b>Delta</b> stehen die Infos über die in der Zwischenzeit in der TClientDataset-Instanz geänderten Daten zur Verfügung

                  Comment


                  • #10
                    Hallo,

                    ich bin's noch einmal. Ich glaube man redet aneinander vorbei.
                    Ich finde keine Lösung für nachfolgendes Problem:

                    <pre>
                    var W:Variant;
                    begin
                    // Übertragen des ClientDataSets in die Tabelle (ohne Provider)
                    if OpenDialog1.Execute then
                    begin
                    // Von der Platte in die Client-Datenmenge laden
                    TempClientDataSet1.FileName := OpenDialog1.FileName; // Filename zuweisen
                    TempClientDataSet1.LoadFromFile;

                    // Für alle Records des ClientDataSets
                    for x := 0 to TempClientDataSet1.RecordCount -1 do
                    begin
                    Table.AppendRecord; // Fügt leeren Record in die Tabelle ein

                    // Die einzelnen Felder der Tabelle übergeben
                    for y := 0 to TempClientDataSet1.Fields.Count -1 do
                    begin
                    // Versuch:
                    // GEHT NICHT, DA "Nur Lesen Eigenschaft"
                    // Table.Database.DataSets[y] := TempClientDataSet1.Fields[y].Value;

                    // OK. (Auslesen wäre möglich)
                    W := DMA_ALLG.TempClientDataSet1.Fields[y].Value;

                    // FRAGE: WIE KÖNNEN DIE FELDER DES RECORDS AUS DEM CLIENTDATASET IN DIE
                    // INTERBASE(5.6) TABELLE DES DATENMODULES ÜBERGEBEN WERDEN ?
                    // AM BESTEN WÄRE EINE ZUWEISUNG DES GESAMTEN RECORDS AUF EINMAL
                    end;

                    Table.Post;
                    end;
                    end;
                    </pre>

                    Der Zweck ist, ohne über das komplizierte Verfahren eines Providers, eine Tabelle im ClientDataSet in eine normale Tabelle der Datenbank zu übertragen.

                    Das ClientDataSet wurde ursprünglich von der betreffenden Tabelle gebildet.

                    Das ClientDataSet soll nicht eingemischt werden, sondern die fixe Tabelle komplett ersetzen.

                    Vor dem Aufruf obiger Prozedur wurde die Tabelle komplett geleert.

                    Gruss<br>Helmu

                    Comment


                    • #11
                      Hallo,

                      das folgende Beispiel demonstriert, wie alle Datensätze der TClientDataSet-Instanz ausgelesen und zur einfacheren Demonstation über eine <b>TMemo</b>-Instanz dargestellt werden. Da man auch für TClientDataSets über den <b>Feldeditor</b> persistente TField-Instanzen anlegen kann, ist der Zugriff auf die einzelnen Spaltenwerte vollkommen identisch mit der Vorgehensweise für TTable/TQuery/TStoredProc:
                      <pre>
                      procedure TFormMain.ButtonExportClick(Sender: TObject);
                      begin
                      ClientDataSetJob.First;
                      while not ClientDataSetJob.Eof do
                      begin
                      Memo1.Lines.Add(Format('Spalte 1: %s; Spalte 2: %s',
                      [ClientDataSetJobJOB_CODE.Value, ClientDataSetJobJOB_TITLE.Value]));
                      ClientDataSetJob.Next;
                      end;
                      end;
                      </pre>
                      Da die Daten in eine InterBase-Datenbank eingefügt werden, sollte eine über TQuery abgesetzt INSERT-Anweisung verwendet werden, die <b>Parameter</b> verwendet. Diese Parameter können vor dem Absetzen der INSERT-Anweisung in der Schleife über die persistenten TFields einfach gefüllt werden. Der Zugriff über <i>TTable.Append</i> ist in dieser Situation aus den verschiedensten Gründen sehr ineffektiv

                      Comment

                      Working...
                      X