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.