TXDataWebDataSet: server response after Post is not applied to the record (new Id not available after ApplyUpdates)
After ApplyUpdates on an inserted record, the field values in the client dataset are unchanged — the identity key generated by the server (and any other server-assigned values such as timestamps) are not visible, so Dataset.FieldByName('Id').AsInteger is still 0 in AfterApplyUpdates.
The server does return the created entity: in XData.Web.Dataset.pas, TXDataUpdateDescriptor.onLoad calls Resolve(Response.Result), which stores it in TRecordUpdateDescriptor.ServerData. But the only consumer of ServerData is the virtual TDataSet.DoResolveRecordUpdate, which is a no-op in the base class, and neither TXDataWebDataSet, TXDataJsonDataset nor TBaseJSONDataset override it. TResolveInfo.Data passed to AfterApplyUpdates/OnRecordResolved is anUpdate.Data — the client's own data, not the server reply — so there is no supported way to reach the returned entity.
In contrast TCustomClientDataSet.DoResolveRecordUpdate (WEBLib.CDS.pas) does exactly this merge for TWebClientDataSet.
Workaround: an interposer of TXDataWebDataSet overriding DoResolveRecordUpdate, taking ServerData and writing each field present into Rows[Bookmark.Data] through FieldMapper.SetJSONDataForField. Suggest TXDataWebDataSet do this by default (or expose ServerData through TResolveInfo).
type
{ Interposer - after ApplyUpdates the entity the server sends back for a
Post/Put (new Ref, Created, Modified, ...) is copied into the record.
TXDataWebDataSet receives that entity but leaves this method as the
TDataSet no-op, so the client record would keep its pre-save values }
TXDataWebDataSet = class (XData.Web.Dataset.TXDataWebDataSet)
protected
function DoResolveRecordUpdate
(AnUpdate : TRecordUpdateDescriptor) : Boolean; override;
end;
Tfrm_Template_Edit = class (TWebForm)
xds_ : TXDataWebDataSet;
src_ : TWebDataSource;
function TXDataWebDataSet.DoResolveRecordUpdate
(AnUpdate : TRecordUpdateDescriptor) : Boolean;
var
LEntity : TJSObject;
LRow : JSValue;
I : Integer;
begin
Result := True;
if (AnUpdate.Status = usDeleted) or
not isObject (AnUpdate.ServerData) or
not isNumber (AnUpdate.Bookmark.Data)
then Exit;
LEntity := TJSObject (AnUpdate.ServerData);
LRow := Rows [Integer (AnUpdate.Bookmark.Data)];
for I := 0 to Fields.Count - 1
do if LEntity.hasOwnProperty (Fields [I].FieldName)
then FieldMapper.SetJSONDataForField (Fields [I], LRow, LEntity [Fields [I].FieldName]);
end;
I had to do something to fix it, so the client could carry on. Doing it this way, I didn't have to modify the source. And it's working fine.
Please do let me know if my understanding is incorrect.