5

I've been having a problem with some code which is supposed to process a TClientDataSet's Delta, which I've boiled down to the following test case.

I have two ClientDataSets, cdsData and cdsDelta, both with a DataSource, DBGrid and DBNavigator, and a TTreeView for displaying the Delta's Old- and NewValues.

In cdsDatas AfterPost, I call the DisplayDelta procedure below.

When I run the app, I change and save the value of one field in csdData from, say, 'PrevValue' to 'UpdatedValue'. My problem is that both the OldValue and NewValue nodes in the Treeview display 'UpdatedValue' and there is no sign of 'PrevValue'. So, how do I get the OldValue correctly?

procedure TDeltaTestForm.DisplayDelta;
var
  Delta : OleVariant;
begin
  Delta := cdsData.Delta;
  if not VarIsClear(Delta) then begin
    cdsDelta.Data := Delta;
  end;
  if cdsData.Modified then
    Caption := 'modified'
  else
    Caption := '';
  Caption := Caption + '/' + IntToStr(cdsData.ChangeCount);
  BuildDeltaTree;
end;

procedure TDeltaTestForm.BuildDeltaTree;
var
  NewNode,
  ChildNode : TTreeNode;
  i,
  ID : Integer;
  Field : TField;
  Value : String;

  function ChildValue(ALabel : String; FieldValue : Variant) : String;
  begin
    Result := ALabel;
    if VarIsClear(FieldValue) then
      Result := Result + '(empty)'
    else
      if VarIsNull(FieldValue) then
        Result := Result + '(null)'
      else
        Result := Result + VarToStr(FieldValue);
  end;
begin
  { Find the current row in the delta dataset }

  ID := cdsData.FieldByName('ID').AsInteger;
  if not cdsDelta.Locate('ID', ID, []) then
    raise Exception.CreateFmt('ID: %d not found in Delta', [ID]);

  TreeView1.Items.BeginUpdate;
  try
    Treeview1.Items.Clear;
    for i:= 0 to cdsDelta.FieldCount - 1 do begin
      Field := cdsDelta.Fields[i];
      NewNode := TreeView1.Items.AddChild(Nil, Field.FieldName);
      ChildNode := TreeView1.Items.AddChild(NewNode, ChildValue('Old: ', Field.OldValue));
      ChildNode := TreeView1.Items.AddChild(NewNode, ChildValue('New: ', Field.NewValue));
      ChildNode := TreeView1.Items.AddChild(NewNode, ChildValue('Cur: ', Field.CurValue));
    end;
    TreeView1.FullExpand;
  finally
    TreeView1.Items.EndUpdate;
  end;
end;
Alex James
  • 696
  • 5
  • 13
  • Hmm. Your code for the child nodes of your treeview looks pretty similar to the code in RecErr.Pas (the supplied error-reconciliation form), so I can't immediately see why that works and yours doesn't. I vaguely remember an NG post by ex-Borlander Mark Edington about something to do with this. Later ... – MartynA Aug 26 '16 at 15:07
  • see provider.pas and TPacketDataSet.InitAltRecBuffers to get the idea how to process delta records – Vladimir Ulchenko Aug 31 '16 at 08:44

1 Answers1

5

I can reproduce your problem. Try this:

Change

for i:= 0 to cdsDelta.FieldCount - 1 do begin
  Field := cdsDelta.Fields[i];

to

for i:= 0 to cdsData.FieldCount - 1 do begin
  Field := cdsData.Fields[i];

You should now find that you get the values for OldValue and NewValue you're expecting. At any rate, I do here.

I confess I'm somewhat puzzled by this - I'd always assumed that you could assign the Delta of one CDS to another and the second one would contain the same field values (Old and New) as the Delta, but evidently not. However, this result chimes with a section of Cary Jensen's excellent "ClientDataSets" ("StatusFilter Versus Delta" in Chapter 6) where he says:

" ... when you load a second CDS with a CDS's Delta, the second CDS does not have a change cache."

Maybe what he means is that what you end up with in the second one is a snapshot of the Delta, not the history of it. I guess that's why your cdsDelta returns the same values in Field.OldValue and Field.NewValue.

MartynA
  • 30,454
  • 4
  • 32
  • 73
  • delta is not original dataset. for every modified record there are two rows in delta packet, first one containing original field values and second one containing modified values – Vladimir Ulchenko Aug 31 '16 at 08:56