January 29, 2014

Delphi XE5 Update 2 has FireDACJSONReflect for DataSnap

There was another hidden gem in the Update 2 of Delphi XE5 (and RAD Studio and C++Builder), namely a new unit to help you working with DataSnap REST clients and FireDAC datasets, both on the client and on the server side.

Only few developers know that there was another hidden gem in the Update 2 of Delphi XE5 (and RAD Studio and C++Builder), namely a new unit to help you working with DataSnap REST servers and FireDAC datasets, both on the client and on the server side. This is a short description of the key elements of this feature (which is rather complex, so I won't be able to cover it in full). Let me start by explaining the issue.

Suppose you want to create a DataSnap server in Delphi, exposing database tables. You basically have two approaches. The first is to rely on the good old IAppServer interface, exposing datasets using provider components and using ClientDataSet components on the client side. That's easy and powerful, but it cannot be used by REST servers, simply because it is a statefull interface (with calls like "give me the next data packet") and required a "heavy" session on the server for each connected user. Also, this is not FDMemTable friendly.

The second approach is to use a RESTfull approach, exposing datasets in your methods. Too bad this is not very efficient and offers no easy way to use an FDMemTable rather than a ClientDataSet on the client side. Plus all updates management has to be done by the developer. So what happens is that we are pushing customers towards two core technologies like DataSnap and FireDAC and we want to make it easier to use them together.

That's why in the Update 2 of XE5 we introduced a new unit, called Data.FireDACJSONReflect . This unit has many capabilities for:

  • Returning one of more datasets in a single call, using an efficient format; multiple datasets in a single call are supported as this is more transactions friendly than doing multiple calls (particularly with databases with a strong implementation of read transactions, like InterBase)
  • Easily caching those datasets in an FDMemTable object on the client, either creating a new structure or adding the result of multiple calls to the same in-memory table
  • Sending back the cached updates (or deltas) for a memory dataset... or more than one in the same call, again for transaction support
  • Easily resolving the update requests on the server
  • Reading and writing data from the datasets and the deltas into custom formats

In this first, simple demo I'm going to show you the code required to send the data from the server to the client and read it on the client. In a future post I'll show an example of the updates management. On the server, rather than returning a TDataSet from a REST function, you have to return a new data type, TFDJSONDataSets. Here is the full code of such a function (using a local temporary table as database, to simplify the demo):

function TServerMethods1.GetJSONData: TFDJSONDataSets;
begin
  Result := TFDJSONDataSets.Create;
  if not FDMemTable1.Active then
    FDMemTable1.LoadFromFile('../../customer.fds')
  else
    FDMemTable1.Active := False;
  TFDJSONDataSetsWriter.ListAdd(Result, FDMemTable1);
end;

As you the see the code creates an instance of the dataset list class, and adds a table to the list using a custom writer. In the same call, you can also give the dataset a name, to make it easier to refer to it in the client code (where you can access the list elements by position of by name).

The client code has the opposite appraoch, using a reader to populate back a memory table:

var
  DSList: TFDJSONDataSets;
begin
  FDMemTable1.Close;
  DSList := ClientModule1.ServerMethods1Client.GetJSONData;
  FDMemTable1.AppendData(
      TFDJSONDataSetsReader.GetListValue(DSList, 0));
  FDMemTable1.Open;
end;

In this case the code empties the memory table of any existing data before adding the new content (calling Close) and uses the first and only dataset, number 0. That's all it takes to transfer any FireDAC dataset on the server side to a FDMemTable on the client side (either desktop or mobile).

I hope this blog post serves as a starting point for your personal exploration if this new and rich unit. The only think I want to underline is that unlike when you return a plain dataset, the REST server return JSON data with mime encoded binary content, so this technique is meaningful only if RAD Studio is used on both ends. For any other client, just use the plain TDataSet JSON mapping. 





 

8 Comments

Delphi XE5 Update 2 has FireDACJSONReflect for DataSnap 

 This looks great.  I have a couple of quesstions.

If an update cannot be resolved on the server side, 
is there somthing like the Reconcile Error dialog 
from the MIDAS days?

What is the advantage of using MIME encodeing? 
Comment by John R. on January 29, 17:06

Delphi XE5 Update 2 has FireDACJSONReflect for DataSnap 

I use my own dataset serialization that simply returns
a TJSONArray of TJSONObjects, one for each record.
That is much much easier to deserialize in C# than the
default TDataSet serialization.
Comment by Anders E. Andersen on January 29, 21:12

Delphi XE5 Update 2 has FireDACJSONReflect for DataSnap 

I will definitely try this out Marco.

But PLEASE think about doing a webinar on this topic.

I cannot find any white papers, videos, or code
samples covering this topic.

The guys at Delphi Experts (Germany) put out a great
video of REST DataSnap, but the client was not FireDAC.

Also, one question: since you are about the only one
with any experience with both of these methods, have
you noticed any performance difference between these
two REST architectures?

Thanks,   
Comment by Keith on January 30, 03:15

Delphi XE5 Update 2 has FireDACJSONReflect for DataSnap 

 Hi Marco,

It looks interesting but need more documentation or example.
What is the way to apply client change on a database server : does a 
simple FDMemTable.ApplyUpdate on client will do the job ?

Best Regards,
Comment by VLDG [] on January 31, 06:46

Delphi XE5 Update 2 has FireDACJSONReflect for DataSnap 

 What’s the point of using Datasnap if it cant handle
database dates (DateTime).Datasnap is limited to
strings as far as I can tell… All DB fields if
retrieved from ClientdataSet end up WideMemo's ....
Comment by Senad on February 10, 11:20

Delphi XE5 Update 2 has FireDACJSONReflect for DataSnap 

Senad, 
  DataSnap has support for several data types: native types, strings, JSON, 
database tables, binary data... so I don't think your comment applies
Comment by Marco Cantu [http://www.marcocantu.com] on February 10, 18:08

Delphi XE5 Update 2 has FireDACJSONReflect for DataSnap 

 Well Marco,not from what I managed to dig out so far.
Every time I tried to insert date into my database I
ended up with "feature not implemented". Tried
retrieving database fields from data set and they all
ended up Wide Memo's! It seems to me Datasnap is
converting everything to string,otherwise I cant
explain Wide Memo's.To insert a date into qlite
database seems like a hopeless task. Did some Googling
and found out many people have similar issues. Dont
believe me? Construct a simple datasnap Client server
with sqlite DB and tell me how it goes.....:)
Comment by Senad on February 11, 07:59

Delphi XE5 Update 2 has FireDACJSONReflect for DataSnap 

Thanks Marco, it looks great!
Can you please post a working example in C++ Builder?
I have some issues casting TFDJSONDataSetsReader::GetListValue(DSList, 
0) as _di_IFDDataSetReference on the client side.

Comment by Giovanni on March 6, 20:25


Post Your Comment

Click here for posting your feedback to this blog.

There are currently 0 pending (unapproved) messages.