0

Does anyone have any experience using SQL Server 's RowVersion column with TClientDataset? Specifically, I need to get the RowVersion value back on insert. It seems to get the RowVersion value back on updates but a newly inserted row it does not.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
avidgoffer
  • 193
  • 1
  • 2
  • 18

1 Answers1

3

This seems to involve a similar problem to when you need to use a CDS with a SqlServer table which has an Identity field, where the value in the field is set by the server, so you don't know it during the CDS-side insert operation. One way to do that is explained in this article http://edn.embarcadero.com/article/20847. It involves assigning a temporary, negative, value to the Identity field in the CDS in the OnNewRecord event, and then using the Refresh method of the CDS to retrieve the server-side identity field value once the new record has been posted to the server by calling ApplyUpdates.

In D7, which I'm using for this answer as a sort of lowest common denominator, this technique seems to work fine to a RowVersion field as well. An extract from a sample project is below. To get it working, I had to define the RowVersion field as an ftBytes field with a size of 8, and to change the ProviderFlags to exclude pfInWhere and pfInUpdate from the AdoQuery's identity field, ID.

The RowVerStr field in the CDS is just to display the value legibly in a TDBGrid.

type
  TForm1 = class(TForm)
    ADOConnection1: TADOConnection;
    ADOQuery1: TADOQuery;
    DataSetProvider1: TDataSetProvider;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    ADOQuery1ID: TAutoIncField;  //  The ID field is an Identity field on the server
    ADOQuery1IntValue: TIntegerField;  //  a user field
    CDS1: TClientDataSet;
    CDS1ID: TAutoIncField;
    CDS1IntValue: TIntegerField;
    CDS1RowVersion: TBytesField;
    CDS1RowVerStr: TStringField;  //  just for display, fkInternalCalc, size = 20
    ADOQuery1RowVersion: TBytesField;
    procedure FormCreate(Sender: TObject);
    procedure CDS1AfterPost(DataSet: TDataSet);
    procedure CDS1AfterDelete(DataSet: TDataSet);
    procedure CDS1NewRecord(DataSet: TDataSet);
    procedure CDS1AfterInsert(DataSet: TDataSet);
    procedure CDS1CalcFields(DataSet: TDataSet);
  public
    FID : Integer;  //  To generate temporary value for CDS identity field
    function NextID : Integer;
  end;

[...]

function GetRowVerString(V : Variant) : String;
var
  i,
  Dim,
  Min,
  Max : Integer;
  i64 : Int64;
begin
  Result := '';
  if not VarIsArray(V) then Exit;
  Dim := VarArrayDimCount(V);
  Max := VarArrayHighBound(V, Dim);
  Min := VarArrayLowBound(V, Dim);

  for i := 0 to 7 do
    Int64Rec(i64).Bytes[i] := V[i];
  Result := IntToStr(i64);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  //  CDS1ID is an ftAutoInc field, so we need to remove its read-only flag so
  //  that we can assign it a temporary negative value in the OnNewRecord event
  CDS1ID.ReadOnly := False;

  AdoQuery1RowVersion.ProviderFlags := AdoQuery1RowVersion.ProviderFlags - [pfInWhere, pfInUpdate];

  CDS1.Open;
  Caption := IntToStr(CDS1.RecordCount);
end;

procedure TForm1.CDS1AfterPost(DataSet: TDataSet);
begin
  if CDS1.ApplyUpdates(0) = 0 then
    CDS1.Refresh;
end;

procedure TForm1.CDS1AfterDelete(DataSet: TDataSet);
begin
  CDS1.ApplyUpdates(-1);
end;

function TForm1.NextID: Integer;
begin
  Dec(FID);
  Result := FID;
end;

procedure TForm1.CDS1NewRecord(DataSet: TDataSet);
begin
  CDS1.FieldByName('ID').AsInteger := NextID;
  CDS1.FieldByName('IntValue').AsInteger := Random(100);
end;

procedure TForm1.CDS1AfterInsert(DataSet: TDataSet);
begin
  CDS1.Post;
end;

procedure TForm1.CDS1CalcFields(DataSet: TDataSet);
begin
  CDS1RowVerStr.AsString := GetRowVerString(CDS1RowVersion.Value);
end;
MartynA
  • 30,454
  • 4
  • 32
  • 73
  • `rowversion` is a [synonym (actually replacement) for `timestamp`](https://learn.microsoft.com/en-us/sql/t-sql/data-types/rowversion-transact-sql). – Victoria Jul 21 '17 at 23:43
  • @MartynA: your solution does work. Seems excessively chatty, but I am not able to implement it in my environment due to restrictions placed upon me beyond my control. – avidgoffer Jul 27 '17 at 13:26