How to refresh modified records to CloneSource before ApplyUpdates?

1.3k views Asked by At

So I have a ClientDataset (cdsM1) with a Nested Detail (cdsD1). I need to print it before do ApplyUpdates, so I clone them (cdsMclone and cdsDclone) and filter the master clone just to show only one master record.

After printing, I need to update the record. At first I tried something like this:

cdsMclone.Edit;
cdsMclone.FieldByName('DATEPRINTED').AsString := Now;
cdsMclone.Post;
cdsMclone.ApplyUpdates(0);

But after this, if I change anything more in the source clientdataset, cdsM1.ApplyUpdates(0) generates a conflict (and by this I mean one that you should respond in a OnReconcileError). But this one should not be showed to the user. Also, using this approach I am sending at least two requests to the Database.

One workaround is using CloneSource property. Example:

cdsMclone.CloneSource.Edit;
cdsMclone.CloneSource.FieldByName('DATEWASPRINTED').AsString := Now;
cdsMclone.CloneSource.Post;

This way, I can call cdsM1.ApplyUpdates(0) without worries. But I really don't like this. Seems the code is changing something it should not do directly. Also, how would the code looks like if in the future I need to change something in the nested detail?

There is any other way to return back changes from the cloned clientdataset to the source?

2

There are 2 answers

1
Deltics On

If your clones are created using CloneCursor() then the original and the clone share the same underlying dataset. They are not separate sets of data, only two different views into the same data.

You do not provide details of the filter used to restrict your original data set to 1 record. However, I suspect that your change to the record is conflicting with the conditions of that filter leading to the conflict error you are seeing.

Using the clone avoids this because the cloned dataset does not have this filter applied. It is a separate view with its own filters. Hence there is no conflict.

There is no issue with modifying the underlying dataset through the cloned cursor in this way. As stated, the clone and the clone source are operating over the same data set, so this would appear to achieve precisely what you wish.

It is an aspect of your application that would benefit from some documentation however, to make the intention and dependencies involved more immediately apparent in the code for those who maintain that code in months and years to come.

3
MartynA On

I only have the version of Rave 5 for D7, but the following works for me, without having to use a filter on the Master table, CloneCursor or anything else like that, to produce a Master-Detail report on only the current row in the Master. So, it should avoid your problem and obviate the need for a work-around.

The MasterConnFirst and MasterConnValidateRow event handlers shown below are set in the Delphi IDE on the TRvDataSetConnection for the master.

TForm1 = class(TForm)
[...]
  cdsMaster: TClientDataSet;
  cdsDetail: TClientDataSet;
[...]
public
  { Public declarations }
  Recs : Integer;
  BM : TBookmark;
end;

procedure TForm1.btnReportClick(Sender: TObject);
begin
  BM := cdsMaster.GetBookmark;  // place a bookmark on the current Master row
  Recs := 0;  // counter for Master records processed by the report
  RvProject.Execute;
end;

procedure TForm1.MasterConnFirst(Connection: TRvCustomConnection);
begin
  // The following moves the cdsMaster to its current row, causing the RvReport to skip the
  // Master rows preceding it.
  cdsMaster.GotoBookmark(BM);
  cdsMaster.FreeBookmark(BM);
end;

procedure TForm1.MasterConnValidateRow(Connection: TRvCustomConnection; var
    ValidRow: Boolean);
begin
  //  This counts the number of Master records processed and returns ValidRow := False
  //  if the Master current row has already been processed.  This will cause
  //  Master rows after our current one to be skipped by the RvReport.

  Inc(Recs);
  if Recs > 1 then
    ValidRow := False;
end;

Anyway, if that works for you, most of the rest of this answer can probably be removed, which I'll do later on.

Notes:

  • The TRvDataSetConnection also has an OnEOF event with an EOF parameter which, according to the Rave5 Developers Guide, you're supposed to be able to set to True once the current master row has been processed, to make the RVReport think the master has no more rows you are interested in. However in Rave 5.0.4, which is the version which came with D7, even assigning a handler to this event causes the report to generate nothing. Presumably a bug in Rave 5.0.4, and may have been fixed in a later version. It's a pity the OnEOF handler doesn't seem to work, as using a ValidateRow handler seems a bit inefficient, considering the report engine still has to iterate over the master records following the current one.

  • There are two properties of the TRvDataSetConnection, DataIndex and DataRows which are supposed to provide an even simpler way of telling it which records to process, by setting DataIndex to the current record's RecNo and DataRows to 1. DataIndex works fine, but the DataRows setting seems to be ignored, presumably another bug in 5.0.4 but maybe it works properly in later versions.