Tutorial: Using a DataSnap Server with an Application

From RAD Studio
Jump to: navigation, search

Go Up to Database and LiveBindings Tutorials


DataSnap technology provides the ability to create Client-Server applications that communicate through the Internet, the local network, or the local host.

The following example demonstrates the use of DataSnap in creating a simple local Client-Server application. Both the client and server applications are implemented in Delphi. After creating the server and activating the connection between the client and server with DataSnap, the client can call methods defined and implemented on the server.

You can implement a server in either Delphi or C++. This example shows both. The client does not need to be implemented in the same language. DataSnap allows you to have a Delphi server and a C++ client—or vice versa.

Key DataSnap Server Components

The main components are:

The TDSServer component is the logical heart of the DataSnap server application. It contains Start and Stop methods for starting and stopping the server. It also contains the AutoStart property. By default, the value of AutoStart is set to True, so the server starts automatically when the application does. You need only one TDSServer component per server application.

The TDSServerClass component represents a server class.

The DataSnap server automatically creates and destroys instances of server classes. The instancing of a server class is controlled by the LifeCycle property of the TDSServerClass component. The LifeCycle property has three possible values: Server, Session, and Invocation.

  • LifeCycle set to Server means that the DataSnap server creates one instance of a server class that is used by all clients connected to the server application. This represents a "singleton" pattern. Be careful when using the Server life cycle, because your server class implementation needs to be thread-safe: you must design the server class so that it can be accessed simultaneously from multiple threads.
  • The default value of LifeCycle is Session. This means that the DataSnap server creates one instance of a server class for every connected client.
  • The third possible value for the LifeCycle property is Invocation. In this case, a server class instance is created and destroyed for every method call arriving from a client, and the state of a server class is not preserved between method calls.

A component is also needed to provide communication between the client and server.

The TDSTCPServerTransport component implements a multithreaded TCP server listening for incoming client connections on multiple threads. This component does not have any events. The Port property indicates the TCP port to be used. By default, it is set to 211. You can also use HTTP for communication between the client and server.

Creating the Server application

  1. Create a new project:
    • For Delphi, choose File > New > VCL Forms Application - Delphi from the main menu.
    • For C++, choose File > New > VCL Forms Application - C++Builder from the main menu.
    Change the Caption property of the form to ServerForm.
  2. Place the following components from the Datasnap Server category of the Tool Palette on the form:
Click the main menu item File > Save All. Save the file as MyServer and save the project as MyServerProj.
At this point, your server application should look like this:
DS App1.jpg


Follow these steps to link the three components together:
  • Select the TDSServerClass component on the form. Set its Server property to the name of your TDSServer component, which is DSServer1 in this example.
  • Select the TDSTCPServerTransport component on the form. Set its Server property to the name of your TDSServer component, DSServer1 in this example.
  1. Add a new unit to your project.
    • For Delphi, select File > New > Unit - Delphi in the main menu.
    • For C++, select File > New > Unit - C++Builder in the main menu.
    Save the unit to the file MyClass.pas for Delphi or MyClass.cpp for C++. This unit contains the implementation of the server's classes you want to use in the client application. In this example, the following code represents the implementation of a class called MyClass that contains one method: the sum of two floating-point numbers:

    Delphi

    unit MyClass;
    
    interface
    uses Classes;
    type
      {$METHODINFO ON}
      TMyClass = class(TComponent)
        function Sum(const A, B: Double): Double;
      end;
      {$METHODINFO OFF}
    
    implementation
    { TMyClass }
    function TMyClass.Sum(const A, B: Double): Double;
    begin
      Result := A + B;
    end;
    
    end.
    

    Note: The {$METHODINFO ON} directive causes the generation of run-time information needed by the Datasnap server, so this is required--it is not just a comment! See METHODINFO directive (Delphi) for more information.

     

    C++

    Place the following code in MyClass.h:

    #include <DSServer.hpp>
    
    class DECLSPEC_DRTTI TMyClass : public TComponent
    {
    private:
    public:
      double Sum(const double A, const double B);
    };
    #endif
    

    Place the following code in MyClass.cpp:

    double TMyClass::Sum(const double A, const double B)
    {
      return A + B;
    }
    

    Note: DataSnap supports RTTI when designing C++ Applications. The DECLSPEC_DRTTI declaration, similar to the Delphi {$METHODINFO ON} directive, causes the generation of run-time information needed by the DataSnap server and is required.

    • For Delphi, add the name of this unit, MyClass, to the uses clause of the server application's unit MyServer.pas.
    • For C++, add this line to MyServer.h:
    #include "MyClass.h"
    
  2. To specify the class you want to call from the client application, define the OnGetClass event for the TDSServerClass component. In the Design tab for MyServer:
    1. Select the TDSServerClass component on the form.
    2. Select the Events tab in the Object Inspector.
    3. Double-click the value for the OnGetClass event.
    4. In this function just generated, add one line of code, which sets PersistentClass:

    Delphi

    procedure TForm1.DSServerClass1GetClass(DSServerClass: TDSServerClass;
      var PersistentClass: TPersistentClass);
    begin
      PersistentClass := TMyClass;
    end;
    

    C++

    void __fastcall TForm1::DSServerClass1GetClass(TDSServerClass *DSServerClass, TPersistentClass &PersistentClass)
    
    {
      PersistentClass = __classid(TMyClass);
    }
    
    If you fail to implement this event, the application raises a TDBXError exception with this message immediately after launching:
    OnGetClass event not set or it did not provide a class reference
    The OnGetClass event has the PersistentClass argument that is passed by reference. In the event handler code, the developer needs to assign PersistentClass a class reference to a server class, as in the example above.
    This is probably the single most important concept to understand about the DataSnap architecture. A class reference is assigned to PersistentClass—not an object reference.

This completes the server application's implementation.

Creating the Client application

  1. To create the client application in the same project group as the server application, right-click the name of your project group in the Project Manager and select Add New Project from the context menu or choose Project > Add New Project from the main menu. The New Items dialog box appears.
    • For Delphi, select the Delphi Projects category, then select VCL Forms Application and click OK.
    • For C++, select the C++Builder Projects category, then select VCL Forms Application and click OK.
    Set both the form's Caption and Name properties to ClientForm. Click the main menu item File > Save All. Save the file as MyClient and save the project as MyClientProj. Save the Project Group to DSServerExample.
  2. Populate the client form with controls used for filling in the input data and writing the result (labels, edit boxes, and a button for calculating the sum) as shown in the figure below. Change the Name property of the TEdit next to the label A to EditA, and change the Name of the TEdit for B to EditB. Set the Name of the Result TEdit to EditResult. Change the Text property of all the TEdit controls to blank. Set the TButton's Caption to Calculate.
  3. Add a TSQLConnection component from the dbExpress category in the Tool Palette. Set the following properties for the TSQLConnection:
    • Driver: Datasnap (from the client's perspective, this provider looks like a connection to a database, but in fact provides connectivity to DataSnap servers.)
    • LoginPrompt: False (optional, to prevent the user name and password dialog from appearing every time the client connects to the server.)
The client form now looks something like this:
DSAppClientForm.png


  1. The most important step in creating the Client-Server application with DataSnap is creating the interface that contains the prototype of all functions implemented on the server. Do this as follows:
    • Select the server project by double-clicking the name of the server project, MyServerProj, in Project Manager.
    • Choose Run > Run Without Debugging from the main menu to run the server. You can minimize the ServerForm dialog that displays.
    • While the server is running, select the client project by double-clicking its name, MyClientProj, in the Project Manager.
    • In the Design tab, set the Connected property of the TSQLConnection component to True.
    • Right-click the TSQLConnection component on the Client form and click Generate DataSnap client classes in the context menu. A new unit is added to your client project, containing information about classes implemented on the server and all the methods contained by these classes.
      • For Delphi, save the new unit as MyDSClient.pas and add this unit's name MyDSClient to the list of units in MyClient.pas.
      • For C++, save the new unit as MyDSClient.cpp. Add the line #include "MyDSClient.h" to the beginning of MyClient.cpp.
  2. Call the desired methods in the client application. In this example, implement the OnClick event for the TButton component to call the Sum method of the DataSnap client unit that you just automatically generated. Here the generated class is TMyClassClient and the button's event handler is TClientForm.CalculateClick (Delphi) or TClientForm::Button1Click (C++).
  3. Delphi

    procedure TClientForm.CalculateClick(Sender: TObject);
    var
      Temp: TMyClassClient;
      A, B: Double;
    begin
      Temp := TMyClassClient.Create(SQLConnection1.DBXConnection);
    try
      A := StrToFloat(EditA.Text);
      B := StrToFloat(EditB.Text);
      EditResult.Text := FloatToStr(Temp.Sum(A, B));
    finally
      Temp.Free;
    end;
    

    C++

    void __fastcall TClientForm::Button1Click(TObject *Sender)
    {
      TMyClassClient *Temp;
      double A, B;
    
      Temp = new TMyClassClient(SQLConnection1->DBXConnection);
      try
      {
        A = StrToFloat(EditA->Text);
        B = StrToFloat(EditB->Text);
        EditResult->Text = FloatToStr(Temp->Sum(A, B));
      }
      __finally
      {
        delete Temp;
      }
    }
    
  4. Finally, build and run the client application. The client form is displayed. Enter decimal numbers in the A and B fields, click the Calculate button, and the sum should appear in the Result field.
  5. If you get an error message containing the text

    Connection cannot be nil. Make sure the connection has been opened.

    you have probably not set the Connected property of the TSQLConnection component to True.

DSAppClientRunning.png


Note: Before terminating the server application, make sure to close all the SQL connections. In this example, set the Connected property of the TSQLConnection component in the client to False. DataSnap does not warn you about pending connections, so even if the server seems to close, it does not close until there are no connections to it. Closing all the client applications does not solve this problem either, because the Delphi IDE can automatically open a connection to the server and browse for the exposed classes and methods.

See Also