Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Dataset ADOQuery in ein Clientdataset kopieren (https://www.delphipraxis.net/213848-dataset-adoquery-ein-clientdataset-kopieren.html)

SusiT 6. Okt 2023 11:33

Dataset ADOQuery in ein Clientdataset kopieren
 
Hallo die Damen und Herren,

mich beschäftigt seit geraumer Zeit ein triviales Problem wie ich vermute.
Ich habe eine ADOQuery, gefüllt mit Daten und möchte diese an ein Clientdataset übergeben.
Dies gelingt aus nicht nachvollziebaren Gründen nicht.

Eine Variante wäre Folgende:

Delphi-Quellcode:
procedure TDataContainer.fillupdataset;
var
  i    : Integer;
  s    : String;

  TempProvider : TDataSetProvider;
  ClientDataSet1 : TClientDataSet;
begin
 
  ClientDataSet1 := TClientDataSet.Create(nil);
  TempProvider := TDataSetProvider.Create(nil);
 
  TempProvider.DataSet := fDataContainerDbThread.ADOQuery;

  i := TempProvider.DataSet.RecordCount; // ist in Ordnung, i ist größer 0
 
  ClientDataSet1.Data := TempProvider.Data;

  i := ClientDataSet1.RecordCount; // hier ist in i immer 0. Die Übergabe klappt nicht

  TempProvider.Free;

end;
Habe schon eine ganze Menge verschiedene Variante probiert aber es klappt nicht.

Was ist der einfachste Weg, Daten in ein CDS zu duplizieren?

Auch wenn ich das CDS mit einem Provider verknüpfe gibt es nicht den gewünschten Erfolg.

Zusatzfrage:
Alle Komponenten werden in Runtime Created. Welchen Owner kann man hier angeben. Dieser Sollte vom Typ TComponent sein, aber TComponent habe ich nicht, da es keine Formularanwendung ist :|

Viele Grüße und vielen Dank

Papaschlumpf73 6. Okt 2023 14:42

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Moin, ich mache das mit dieser proc. TDM ist einfach nur mein DataModule

Delphi-Quellcode:
procedure TDM.CopyDataSetToCDS(Source: TDataSet; Dest: TClientDataSet; CloseSource: boolean);
var dsProvider: TDataSetProvider;
begin
{GetText in der Source funktioniert nicht}
{Filter in der Source funktioniert}
dsProvider:=TDataSetProvider.Create(nil);
try
 dsProvider.DataSet:=Source;
 Dest.SetProvider(dsProvider);
 Dest.Open;
finally FreeAndNil(dsProvider); end;
if CloseSource then Source.Close;
end;

SusiT 6. Okt 2023 19:10

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Besten Dank für dein Feedback.
Ich habe eine kleine Demo gemacht und bekomme die gleichen Probleme wie vorher
Delphi-Quellcode:
unit DM;

interface

uses
  System.SysUtils, System.Classes, Datasnap.Provider, Data.DB, Datasnap.DBClient;

type
  TDataModule1 = class(TDataModule)
    cds1: TClientDataSet;

  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    procedure testemal(ds : TDataSet);
    procedure CopyDataSetToCDS(Source: TDataSet; Dest: TClientDataSet; CloseSource: boolean);
  end;

var
  DataModule1: TDataModule1;

implementation

{%CLASSGROUP 'System.Classes.TPersistent'}

{$R *.dfm}

procedure TDataModule1.testemal(ds : TDataSet);
var
  a : Integer;
begin

  a := ds.RecordCount;
  // in a steht 17, das ist auch korrekt

  DataModule1.CopyDataSetToCDS(
      ds,
      cds1,
      false
  );

  a := cds1.RecordCount;
  // in a steht 0. Der Übertrag hat nicht geklappt?!
end;

procedure TDataModule1.CopyDataSetToCDS(Source: TDataSet; Dest: TClientDataSet; CloseSource: boolean);
var dsProvider: TDataSetProvider;
begin
{GetText in der Source funktioniert nicht}
{Filter in der Source funktioniert}
dsProvider:=TDataSetProvider.Create(nil);
try
 dsProvider.DataSet:=Source;
 Dest.SetProvider(dsProvider);
 Dest.Open;
finally FreeAndNil(dsProvider); end;
if CloseSource then Source.Close;
end;
end.
Ich erwarte an der Stelle

Delphi-Quellcode:
a := cds1.RecordCount
das in a die gleiche Anzahl der Records enthalten ist, wie im dataset ds.

Habe ich hier irgendeinen Gedankenfehler?

Das ganze ist in einem Datamodul und die CDS liegt auf der Form.
Der Aufruf von testemal kommt von aussen, aber im dataset ds ist injedemfall Inhalt.

Es bleibt spannend :roll:

SusiT 6. Okt 2023 21:00

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Nach ewigen probieren bin ich zu einer Variante gelangt die anscheinend funktioniert. Es ist mir allerdings ein totales Rätsel warum das so ist.
Delphi-Quellcode:
procedure TDataModule1.CopyDataSetToCDS(Source: TDataSet; var Dest: TClientDataSet; CloseSource: boolean);
var dsProvider: TDataSetProvider;
  I: Integer;
  a: Boolean;
begin
{GetText in der Source funktioniert nicht}
{Filter in der Source funktioniert}
dsProvider:=TDataSetProvider.Create(self);
try
 dsProvider.DataSet:=Source;

 a := Dest.Data = dsProvider.Data;

 Dest.SetProvider(dsProvider);
 Dest.Open;

finally FreeAndNil(dsProvider); end;
if CloseSource then Source.Close;
end;
end.
Wenn exakt an der Stelle oben im Code

Delphi-Quellcode:
 a := Dest.Data = dsProvider.Data;
eingefügt wird, dann erhalte ich auch wie erwartet an folgender Stelle den Wert 17

Delphi-Quellcode:
a := cds1.RecordCount;
Allerdings, die zugefügte Codezeile ergibt keinen Sinn.
Dest.Data ist an der Stelle immer null, a wird immer False.
trotzdem passiert irgendwas, dass dazu führt, dass im cds aufeinmal alle Daten drin sind.

Ich könnte auch

Delphi-Quellcode:
 if Dest.Data = dsProvider.Data then
 begin
 end;
ergänzen, das hat den selben positiven Effekt.

Ein Timingproblem scheint es nicht zu sein. Ich habe an der Stelle das Programm durch eine Schleife im Ablauf verzögert. Das hat nichts gebracht.

Tja, da weiß ich nicht weiter. Vermutlich fehlt irgendein Schritt mit dem Dest oder dem Provider, der durch die eingefügte Zeile im Hintergrund ausgelöst wird.

Viele Grüße und gute Nacht

Papaschlumpf73 6. Okt 2023 23:36

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Keine Ahnung, was du da gemacht hast. Hier vielleicht noch ein paar Hinweise - zumindest mache ich es so und es funktioniert immer. Bevor der die Copy-Proc aufgerufen wird sollte:
  • der Datensatzzeiger der Source auf Record 1 stehen; nicht am Ende
  • das ClientDataSet bereits alle Datenfelder enthalten
  • das ClientDataSet leer und geschlossen sein
Wenn das alles nicht hilft, empfiehlt es sich mit einem vereinfachten Beispiel anzufangen, ohne irgendwelche Schleifen, Filter, Calcfields und was da vielleicht sonst noch drin ist.

Olli73 7. Okt 2023 12:31

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Du scheinst, wie Papaschlumpf73 schreibt, auf dem letzten Datensatz zu stehen.

Probiere mal Folgendes aus:

Delphi-Quellcode:
procedure TForm1.CopyDataSetToCDS(Source: TDataSet; Dest: TClientDataSet; CloseSource: boolean);
var
  dsProvider: TDataSetProvider;
  inscount: Integer;
begin
  dsProvider := TDataSetProvider.Create(nil);
  try
   dsProvider.DataSet := Source;

   dsProvider.GetRecords(-1, inscount, 0); // !!! Lade alle Records !!!

   Dest.SetProvider(dsProvider);
   Dest.Open;
  finally
    FreeAndNil(dsProvider);
  end;
  if CloseSource then
    Source.Close;
end;

SusiT 7. Okt 2023 16:01

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Ich kann mich nur bei euch bedanken.

Sowohl den Sourcedatensatz auf .First

setzen als auch die GetRecords Variante führen zum gewünschten Ergebnis.

Soviele verschiedene Varianten die ich probiert habe aber auf die triviale Idee bin ich nicht gekommen.

Vielen Lieben Dank an euch!

SusiT 8. Okt 2023 11:16

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Hallo, ich muss euch doch nochmal belästigen.

Grundsätzlich dachte ich, dass es funktioniert. Das tut es auch allerdings nur im ersten Durchlauf. Danach wird das ZielCds nicht erneut aktualisiert.

Folgende 2 Varianten habe ich erstellt:

Einmal mit einem CDS das global erstellt wurde und bei jedem Durchlauf geleert werden soll:

Delphi-Quellcode:

testCDS_global : TClientDataSet;
...
testCDS_global := TClientDataSet.Create(self);
Delphi-Quellcode:
procedure TDataContainer.fillupdataset;
var
  a,b : Integer;
begin
  try
    if testCDS_global.Active then
    begin
      testCDS_global.Open;
      testCDS_global.EmptyDataSet;
    end;
  except
    a:=a;
  end;

  try
    // a = Anzahl der Einträge im ADOquery dataset
    a := fDataContainerDbThread.ADOQuery.RecordCount;

    CopyDataSetToCDS(
      fDataContainerDbThread.ADOQuery,
      testCDS_global,
      false
    );

    b := testCDS_global.RecordCount;
  finally

  end;
end;
Diese Prozedur füllt das testCds nur beim ersten Durchlauf, sobald das CDS einmal erstellt, gefüllt und beim Durchlauf EmptyDataSet gemacht wurde, dann klappt das nicht mehr.


Folgende Prozedur führt zu einer Aktualisierung bei jedem Durchlauf:

Delphi-Quellcode:
procedure TDataContainer.fillupdataset2;
var
  a,b : Integer;
  testCDS_lokal : TClientDataSet;
begin
  testCDS_lokal := TClientDataSet.Create(nil);

  try
    // a = Anzahl der Einträge im ADOquery dataset
    a := fDataContainerDbThread.ADOQuery.RecordCount;

    CopyDataSetToCDS(
      fDataContainerDbThread.ADOQuery,
      testCDS_lokal,
      false
    );

    b := testCDS_lokal.RecordCount;
  finally
    FreeAndNil(testCDS_lokal);
  end;
end;
Heißt, es muss einen Unterschied zwischen einem neu createden CDS und einem CDS das mit EmptyDataSet behandelt wurde geben.
Ich komme nicht drauf :cyclops:


Vollständigkeitshalber:

Delphi-Quellcode:
procedure TDataContainer.CopyDataSetToCDS(Source: TDataSet; Dest: TClientDataSet; CloseSource: boolean);
var
  dsProvider: TDataSetProvider;
  a,b : Integer;
begin
  dsProvider := TDataSetProvider.Create(nil);
  try
    Source.First;
    dsProvider.DataSet := Source;

    dsProvider.GetRecords(-1, a, 0); // !!! Lade alle Records !!!

    Dest.SetProvider(dsProvider);
    Dest.Open;
  finally
    FreeAndNil(dsProvider);
  end;
  if CloseSource then
    Source.Close;
end;
Zusatzfrage:
Wenn die Variable dsProvider auf FreeAndNil gesetzt wird, woher kennt das das Sourcedataset im Anschluss dann noch seinen Provider? Das scheint an dieser Stelle egal zu sein? Der dsProvider ist ja ohnehin nur in dieser Prozedur verfügbar..

Papaschlumpf73 8. Okt 2023 11:38

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Das hier ist schon mal nicht so toll.

Delphi-Quellcode:
   if testCDS_global.Active then
    begin
      testCDS_global.Open;
      testCDS_global.EmptyDataSet;
    end;
Wenn das CDS aktiv ist, dann soll er es öffnen? Nein, das kann es nicht sein.
Das würde ich erstmal ändern in:

Delphi-Quellcode:
   if testCDS_global.Active then
   begin
     testCDS_global.EmptyDataSet;
     testCDS_global.Close;
   end;
Dann hast du für die Copy-proc ein leeres, geschlossenes ClientDataSet.

SusiT 8. Okt 2023 13:13

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
An der Stelle habe ich auch schon probiert. Ich bekomme beim Close immer einen "schwerwiegenden Fehler" angezeigt.
Aus diesem Grund habe ich das Close wieder entfernt.

Aber grundsätzlich macht das Sinn was du schreibst.

SusiT 8. Okt 2023 14:05

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Um den schwerwiegenden Fehler beim close zu beseitigen habe ich nun den dsProvider in CopyDataSetToCDS auch global hinterlegt (so wie das cds).
Jetzt scheint es so abzulaufen wie ich mir das wünsche.

Ich hoffe das war es :spin2:

Papaschlumpf73 8. Okt 2023 14:38

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Wenn du die globale Variante nimmst, solltest du unbedingt LogChanges:=false für das Clientdataset direkt nach dessen Create aufrufen. Das verhindert, dass alle Änderungen protokolliert werden und der Arbeitsspeicher immer voller wird.

SusiT 8. Okt 2023 15:17

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Besten Dank, habe es mit eingebaut.
:thumb:

haentschman 9. Okt 2023 06:50

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Moin...8-)
[nur meine Meinung]
Der Hickack mit dem CDS, und ADO als solches :zwinker:, riecht für mich nach "Workaround". :?
Zitat:

gefüllt mit Daten und möchte diese an ein Clientdataset übergeben
Warum? Was möchtest du mit den Daten machen? Vieleicht gibt es elegantere Methoden um das Ziel zu erreichen. :wink:

Papaschlumpf73 9. Okt 2023 10:02

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Auch wenn die Frage jetzt nicht an mich ging: In unseren Datenbanken müssen die Daten einiger Tabellen verschlüsselt werden - so dass auch die IT nicht dran kommt. Diese Daten werden in der Delphi-Anwendung per FeldSetText/FieldGetText während des Schreibens oder Lesens ver- bzw. entschlüsselt. So werden die Daten immer sauber angezeigt und man muss sich nicht weiter um die Verschlüsselung kümmern. Wenn man aber nach Textfeldern filtern oder im Grid sortieren will, stört die Verschlüsselung. In solchen Fällen kopiere ich die Daten in ein CDS und entschlüssele dort zunächst alle Datensätze lokal.

SusiT 11. Okt 2023 22:01

AW: Dataset ADOQuery in ein Clientdataset kopieren
 
Ich nutze das CDS nur um Daten zwischenzuspeichern. Ein DB Thread läuft, führt in regelmäßigen Abständen eine Datenbankabfrage durch und speichert die geladenen Daten threadsicher im CDS. Anschließend gibt es andere, die auf das CDS zugreifen und die Daten in Ihren eigenen Wirkungsbereich kopieren und damit arbeiten. Ich vermeide damit, dass jeder einzelne auf die Datenbank zugreift und erhoffe mir damit eine verbesserte performance. Außerdem bringt ein CDS von Haus aus ganz nette Features mit. Das ganze im XE2.

Das mit der Verschlüsselung hatte ich auch schon mal durchdacht. Danke für den Input. Damit ist der Punkt filtern und suchen tatsächlich möglich. Dies war auch bei mir ein Störfaktor beim Durchdenken :thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:17 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz