DataSnap over SOAP

DataSnap over SOAP

Now that you have a reasonably good idea how to build a SOAP server and a SOAP client, let's look at how to use this technology in building a multitier DataSnap application. You'll use a Soap Server Data Module to create the new web service and the SoapConnection component to connect a client application to it.

Building the DataSnap SOAP Server

Let's look at the server side first. Go to the Web Services page of the New Items dialog box and use the Soap Server Application icon to create a new web service, and then use the Soap Server Data Module icon to add a DataSnap server-side data module to the SOAP server. I did this in the SoapDataServer7 example (which uses the Web App Debugger architecture for testing purposes). From this point on, all you do is write a normal DataSnap server (or a middle-tier DataSnap application), as discussed in Chapter 16 ("Multitier DataSnap Applications"). In this case, I added InterBase access to the program by means of dbExpress, resulting in the following structure:

object SoapTestDm: TSoapTestDm
  object SQLConnection1: TSQLConnection
    ConnectionName = 'IBLocal'
  object SQLDataSet1: TSQLDataSet
    SQLConnection = SQLConnection1
    CommandText = 'select * from EMPLOYEE'
  object DataSetProvider1: TDataSetProvider
    DataSet = SQLDataSet1

The data module built for a SOAP-based DataSnap server defines a custom interface (so you can add methods to it) inheriting from IAppServerSOAP, which is defined as a published interface (even though it doesn't inherit from IInvokable).


Delphi 6 used DataSnap's standard IAppServer interface for exposing data via SOAP. Delphi 7 has replaced the default with the inherited IAppServerSOAP interface, which is functionally identical but allows the system to discriminate the type of call depending on the interface name. You'll see shortly how to call an older application from a client built in Delphi 7, because this process is not automatic.

The implementation class, TSoapTestDm, is the data module, as in other DataSnap servers. Here is the code Delphi generated, with the addition of the custom method:

  ISampleDataModule = interface(IAppServerSOAP)
    function GetRecordCount: Integer; stdcall;
  TSampleDataModule = class(TSoapDataModule, ISampleDataModule,
      IAppServerSOAP, IAppServer)
    DataSetProvider1: TDataSetProvider;
    SQLConnection1: TSQLConnection;
    SQLDataSet1: TSQLDataSet;
    function GetRecordCount: Integer; stdcall;

The base TSoapDataModule doesn't inherit from TInvokableClass. This is not a problem as long as you provide an extra factory procedure to create the object (which is what TInvokableClass does for you) and add it to the registration code (as discussed earlier, in the section "Exposing an Existing Class as a Web Service"):

procedure TSampleDataModuleCreateInstance(out obj: TObject);
  obj := TSampleDataModule.Create(nil);
    TSampleDataModule, TSampleDataModuleCreateInstance);

The server application also publishes the IAppServerSOAP and IAppServer interfaces, thanks to the (little) code in the SOAPMidas unit. As a comparison, you can find a SOAP DataSnap server built with Delphi 6 in the SoapDataServer folder. The example can still be compiled in Delphi 7 and works fine, but you should write new programs using the structure of the newer; an example is in the SoapDataServer7 folder.


Web service applications in Delphi 7 can include more than one SOAP data module. To identify a specific SOAP data module, use the SOAPServerIID property of the SoapConnection component or add the data module interface name to the end of the URL.

The server has a custom method (the Delphi 6 version of the program also had one, but it never worked) that uses a query with the select count(*) from EMPLOYEE SQL statement:

function TSampleDataModule.GetRecordCount: Integer;
  // read in the record count by running a query
  Result := SQLDataSet2.Fields[0].AsInteger;

Building the DataSnap SOAP Client

To build the client application, called SoapDataClient7, I began with a plain program and added a SoapConnection component to it (from the Web Services page of the palette), hooking it to the URL of the DataSnap web service and referring to the specific interface I'm looking for:

object SoapConnection1: TSoapConnection
  URL = 'http://localhost:1024/SoapDataServer7.soapdataserver/' +
  SOAPServerIID = 'IAppServerSOAP - {C99F4735-D6D2-495C-8CA2-E53E5A439E61}'
  UseSOAPAdapter = False

Notice the last property, UseSOAPAdapter, which indicates you are working against a server built with Delphi 7. As a comparison, the SoapDataClient (again, no 7) example, which uses a server created with Delphi 6 and recompiled with Delphi 7, must have this property set to True. This value forces the program to use the plain IAppServer interface instead of the new IAppServerSOAP interface.

From this point on, you proceed as usual, adding a ClientDataSet component, a DataSource, and a DBGrid to the program; choosing the only available provider for the client dataset; and hooking the rest. Not surprisingly, for this simple example, the client application has little custom code: a single call to open the connection when a button is clicked (to avoid startup errors) and an ApplyUpdates call to send changes back to the database.

SOAP versus Other DataSnap Connections

Regardless of the apparent similarity of this program to all the other DataSnap client and server programs built in Chapter 16, there is a very important difference worth underlining: The SoapDataServer and SoapDataClient programs do not use COM to expose or call the IAppServerSOAP interface. Quite the opposite—the socket- and HTTP-based connections of DataSnap still rely on local COM objects and a registration of the server in the Windows Registry. The native SOAP-based support, however, allows for a totally custom solution that's independent of COM and that offers many more chances to be ported to other operating systems. (You can recompile this server in Kylix, but not the programs built in Chapter 16.)

The client program can also call the custom method I've added to the server to return the record count. This method could be used in a real-world application to show only a limited number of records but inform the user how many haven't yet been downloaded from the server. The client code to call the method relies on an extra HTTPRIO component:

procedure TFormSDC.Button3Click(Sender: TObject);
  SoapData: ISampleDataModule;
  SoapData := HttpRio1 as ISampleDataModule;
  ShowMessage (IntToStr (SoapData.GetRecordCount));

Part I: Foundations