You are on page 1of 16

white paper

Advantage Database Server and


DataSnap Good Friends!

Bob Swart, Bob Swart Training & Consultancy

www.sybase.com

TABLE OF CONTENTS
1 DataSnap Server
3 DataSnap Container Unit
4 Registration Table
5 ServerMethodsUnit
5 TAdsTable: Events
6 Query Master-Detail
9 DataSnap Client
10 TSqlServerMethod
12 TDSProviderConnection
14 DataSnap

In this article, I will demonstrate that DataSnap, the multi-tier application framework found in the Enterprise
editions of Delphi and C++Builder, is not limited to the databases and data access technologies found in these
development environments, but can also be used and integrated with the Advantage Database Server and Advantage
TDataSet components.
In order to demonstrate this, I will extend an existing event registration application by exposing the data using
a DataSnap Server, and also allowing special users to make changes to this data (using the DataSnap role-based
authentication functionality on top of the Advantage Database Server).

DataSnap Server
In this article, Im using Delphi XE Enterprise, but similar steps can be done using C++Builder Enterprise. Once
Delphi is started, do File | New Other, and in the Object Repository locate the DataSnap Server category with the
DataSnap Server wizards. There are three possible wizards we can use, producing web-based or stand-alone DataSnap
Servers. There are basically two kinds of protocols that the DataSnap Servers can use: HTTP(S) and TCP/IP. HTTPS is
supported only by creating an ISAPI DLL deployed on IIS (with an SSL certificate), while HTTP(S) and TCP/IP can also be
secured using DataSnap filters. TCP/IP is faster than HTTP, so for the example DataSnap application I decided to use
the standard DataSnap Server wizard, producing a stand-alone DataSnap Server application (with a choice between
HTTP and TCP/IP as transport protocol in one of the wizard pages).

The wizard offers three choices for our DataSnap Server application. A VCL Forms Application is the easiest one to
start with, but not very practical when it comes to deployment. The Console Application isnt even as intuitive to use
as the VCL Forms Application, but the best choice at this time also when it comes to deployment is the Service
Application. A Windows Services can be installed and configured to start automatically (as soon as the machine itself
is started), without the need to manually start the application.

The next page of the Wizard can be used to specify the communication protocol. We have a choice between HTTP
and TCP/IP. Since the latter is much faster, we only need TCP/IP as protocol.
Authentication (who is using the system?) and Authorization (what is this user allowed to do?) are also useful
features, so we should check them as well.
Finally, we need some place to specify and store our Server Methods (inside a Server Methods Class), and it doesnt
hurt to include a few sample server methods either.

The next page of the Wizard can be used to specify the port to use for the TCP/IP transport. The default port is 211,
which is fine for this demo application. Note, however, that in real-world situations you may want to use a different
port (to make it harder for uninvited visitors to guess the TCP/IP port that your DataSnap Server is listening to). Also,
do not forget to open up your firewall, and your router or gateways firewall to allow incoming TCP/IP traffic on this
specific port, otherwise the DataSnap Server may get very lonely without any incoming messages.

The next page of the DataSnap Server wizard can be used to select the choice for the Server Methods class. By
default, a simple TComponent base class is suggested, but we can only implement server methods as part of this class,
and cannot expose TDataSetProviders (a powerful functionality of DataSnap). For the latter to work completely, we
should select not just the TDataModule, but in fact the special TDSServerModule base class (which implements the
IAppServer interface for us).

If we click on Finish, a new DataSnap Server project is created. We can save the project as RegistrationServer,
containing ServerContainerUnit1.pas and ServerMethodsUnit1.pas.

DataSnap Container Unit


The ServerContainerUnit1.pas contains a TServerContainer1 class with four components placed on it: TDSServer,
TDSServerClass, TDSTCPServerTransport and TDSAuthenticationManager.

The TDSServer component is the engine of the DataSnap Server application. With methods to explicitly Start,
Pause and Stop the engine. By default, the AutoStart property is set to True, to ensure that the engine is started as
soon as the application itself is run. In our case, this means that the DataSnap engine is active as soon as the service
application is started.
The HideDSAdmin property of the TDSServer component is set to False by default, but we should change that to
true when its time to deploy the application.
The TDSServerClass component is used to tell the DataSnap Server which server class to generate for the incoming
requests, using the OnGetClass method. The LifeCycle property can be set to Server, Session and Invocation to control
if the specific server class is generated only once (for all incoming connections), once for each session, or once for
each request. The default is set to Session, which means the server class instance can store state information (like
the current record), but this does result in a less scalable solution. With a LifeCycle set to Invocation, the DataSnap
application can handle far more requests, but this may require a bit more work (which falls beyond the scope of this
example, but is covered in my DataSnap Development Essentials courseware manual for example).
The TDSTCPServerTransport component is responsible for the TCP/IP communication between the clients and the
DataSnap server application. We can configure the BufferKBSize property, by default set to 32, to increase performance
when passing large sections of data (like big datasets). Note that the buffer size should be increased at the client side
as well for best effect.
The TDSTCPServerTransport component also has a property AuthenticationManager that points to the
TDSAuthenticationManager component
The TDSAuthenticationManager component is responsible for both the Authentication (who is the client
connecting to the server) and the Authorization (which server classes and/or server methods can be executed by
this user?). We can control these by implementing the OnUserAuthenticate and OnUserAuthorize event handler (by
default they both return True, meaning that anyone can login and has access to all classes and server methods). Well
get back to this option later in the article, when the Server Methods are implemented.
Before we continue with the DataSnap Server Methods Unit, we should first take a closer look at the database
tables in side the Advantage Database Server that we want to use.

Registration Table
Lets take a look at the Event Registration table that Ive been using for a number of my Delphi and Advantage
Database Server events in The Netherlands in the past few years. Generally, the table had the following layout, as
shown in the Advantage DataArchitect here:

This table holds the information for one specific developer, attending an event. However, for each event, I would
need to start with an empty table again. And some developers would need to re-register for new events (even if they
might have been registered, and therefore known, for a previous event). In order to extend the possibilities of the
registrations, and to allow people to login and register for another event without having to fill in all fields from the
registration table again, I decided to extend the data model with two other tables: Events and EventRegistrations
(the initial Registration table could have been renamed to Developers, but I decided to keep the old name, so old
applications wouldnt break).
The Events table holds information about the specific events that the developers want to register to, like my Delphi
Workshops in The Netherlands, or Advantage Database Server seminars for example. This table should hold fields for
the name of the event, the date, location and optionally a URL with more information.

Since a registered developer can attend one or more events, and an event is usually attended by one or more
attendees, we need a N:M table to connect these two tables, which is done in the table EventRegistrations. This table
contains only two fields: ID from the Registration table and EventID from our new Events table.
Assuming we have one table with registered developers of a single event, we can now create a new record in the
Events table for that (past) event, as well as a set of records in the EventRegistration table to hook up all registered
users to that event. I leave that as exercise for the reader, but it was required to get the next example code to work
and return a useful result for the demo.

ServerMethodsUnit
Time to move to the Server Methods Unit, where we can finally add some data access components to connect to
the Events data dictionary and the tables inside it.
We start by placing an TAdsConnection component on the TServerMethods1 class. Since we created a Data
Dictionary called Events, we can use the AliasName property to connect to this Data Dictionary on the local machine
(this assumes that the DataSnap Server and the Advantage Database Server run on the same machine, but we can
also connect to a remote database of course).
After the AliasName property has been set, I always also set the LoginPrompt property to False. Note that this is
not really required, since the LoginPrompt property has no effect when it turns out that the database doesnt need a
password (quite handy, because it doesnt ask for a username/password when none is needed, however, this is only
the case with data aware controls and a dictionary connection).
Finally, we can check if we can connect to the tables by setting the IsConnected property to True.
The TAdsConnection component has a few more interesting properties, worthy of a mention. I often get in trouble
when I open a Delphi project from machine X on machine Y, and the database (from machine X) cannot be found on
machine Y. Using other data access technologies, with an active table, query or database connection at design-time,
this usually means that I have to wait for a connection timeout before I can continue. With the TAdsConnection
component, the value of the IsConnected property used at design-time doesnt have to be stored. The property
StoreConnected can be used to control the saving of the IsConnected value to the DFM file. Quite handy, since this will
avoid these time-out errors. Note that we need to set StoreConnected to False (since its set to True by default), but at
least we only need to do that once, and we dont need to remember to close any database connections before we save
or close the project.
Another helpful property is the CompressionType property, by default set to ccAdsCompressionNotSet, but we can
also assign values ccAdsCompressionInternet (to indicate that data transfer to the internet should be compressed) or
ccAdsCompressionAlways (to indicate that all data transfers should be compressed).. Compression results in less data
that needs to be transferred, but takes a little longer to process (the data needs to be compressed and decompressed).
For that reason, its not very useful to use compression on a Local connection. For real Client/Server or web application
this is a nice property to help minimize the bandwidth usage.
The TAdsConnection component also has UserName and Password properties, in case the database needs an
authentication. We can also pass this information using the OnLogin event handler, where we can pass the UserName
and Password that the user has entered on screen for example.
Another nice property is ReadOnly, which we can use in situations where its clear in advance that no changes to
the data should (and will) be made, but only read (SELECT) operations are allowed. By setting the ReadOnly property
to True, we are assured that no UPDATE, INSERT or DELETE commands will be performed, and that the data will indeed
not change.

TAdsTable: Events
The TAdsTable component can be compared to the good-old BDE TTable component, for those readers who still
use the BDE (and should really migrate away from it). When using a TAdsTable component, we should point the
AdsConnection property to a TAdsConnection component, and then we can select a suitable table by picking one from
the list we get for the TableName property. The Active property works just like the normal TTable, and can be used to
view live data at design-time.
For our example, we should select the Events table as value for the TableName property for a TAdsTable component,
that we can give the name atEvents.

To return the contents of this TAdsTable from the server methods unit in the DataSnap server to the DataSnap
clients, we can write a simple server method, implemented as follows:
function TServerMethods1.GetEvents: TDataSet;
begin
atEvents.Open;
Result := atEvents;
end;

All we need to do is open the atEvents TAdsTable (pointing to the Events able) and assign it to the result of the
GetEvents server method. This will return the dataset in a way that it can only be read and not modified (since its the
result of a function), which is ideal to produce just a list of events.

Query Master-Detail
Another, and slightly more complex, example involves a master-detail query. This time, we start with a TAdsQuery
component, the counterpart of the BDE TQuery, and ideal replacement if you still use the BDE for example. The
TAdsQuery needs to be hooked up to the TAdsConnection component, using the AdsConnection property.
The TAdsQuery component has an SQL property that can be assigned a SELECT command, like the following to
select the EventID, EventName, EventDate, Location, and URL from the Events table.

If youve placed a TAdsQuery component on the Server Methods unit, you can rename the TAdsQuery to aqEvents,
and set the IndexFieldNames to EVENTID. We also need a TDataSource component, named dsEvents, connected to the
aqEvents, in order to hook up the detail table in a minute.
We can then place a second TAdsQuery component on the Server Methods unit. Where the master is used to return
all events, in the aqEvents, the second query will be the detail, returning all registered users for the (master) event.
We should give the second TAdsQuery the name aqRegistration, and can write the following SQL command to
return the ID, FirstName, LastName, Address, Postcode, City, Country, Company, Email, Phone, SDN (membership
number) and Delphi (version) from the Registration table, connecting the two ID fields (from the Registration table
and the EventRegistration table), using the EventID field from the EventRegistration table to filter out the events
based on the EventID parameter, which can be obtained from the master aqEvents query.

In order to get the value of the :EventID parameter, we must connect the aqRegistrations DataSource property to
the dsEvents TDataSource (which in turn was connected to the aqEvents TAdsQuery, remember?).
The single parameter EventID will automatically be filled in with the current value of the EventID field in the master
query aqEvents, selecting only the records from the detail query aqRegistration that belong to that specific event.
Finally, we need a TDataSetProvider to expose the master and detail query records from the Server Methods unit in
the DataSnap server to the DataSnap clients. Give the TDataSetProvider the name dspEventRegistrations, and connect
its DataSet property to the aqEvents master query (which will automatically also include the aqRegistrations detail
records in a so-called nested dataset).

We can now compile the DataSnap Server project, and run it. However, since the project is a Windows service
project, running it will have no effect. Instead, we should run it with the install command-line option in order to
install it as a Windows service. The easiest way to do this from the Delphi IDE is to go to the Project Options dialog,
and specify the install Parameter.

Now, when we run the service, it will just install it. After a message from the firewall, you will get the confirmation
dialog that the service is installed.

Being installed, the service doesnt start automatically. We need to go to the Computer Management Console to
explicitly start the service for the first time (or just reboot the computer, since the startup type is set to Automatic).

However, before we start it, we can enable the allow this service to interact with desktop option, which will be
a big help if we want to display error messages (although these are best logged using the third-party tool CodeSite,
which is now included with Delphi XE as CodeSite Express edition).

Finally, we can explicitly start the DataSnap service, and can prepare to build the DataSnap client project to retrieve
8

and work with the data from the Advantage database.

DataSnap Client
For the DataSnap Client application, we need a new project. This can be any kind of project, from a regular VCL
application to an IntraWeb application or even a console application or whatever type you want to use. For this article,
the easiest way to demonstrate the required steps and implementation details, is to use a VCL forms application.
Adding one to the existing project group (with the RegistrationServer already included as first project) makes it easy
to switch from working on one project to working on the other.
The new project is saved in RegistrationClient, with the main form in file ClientForm.pas
In order to connect to the DataSnap server in the RegistrationServer service, we need a TSQLConnection component.
This can be placed on a data module, or in this simple example on the main form itself. This TSQLConnection
component has a Driver property that must be set to DataSnap. This will allow us to expand the Driver property, and
configure all DataSnap specific settings like the BufferKbSize (32 by default), the CommunicationProtocol (set to tcp/
ip), the HostName (set to localhost by default), the Port (211 by default), and the DSAuthUser and DSAuthPassword in
case we included Authentication to the DataSnap Server.
Obviously, the HostName subproperty should point to the real server where the DataSnap Server (or service) is
deployed, and not localhost which points to the same machine where the client runs; the development machine.
Also, in real-world situations, the Port will be set to a different number than 211, and we must ensure that both the
DataSnap Server and DataSnap Client agree on the same port number of course.
In our situation, the tcp/ip connection on port 211 of localhost needs no further customization, so we can set the
Connected property of the TSQLConnection to True to verify that we can make a connection to the DataSnap Server.
For our example project, the properties of the TSQLConnection are configured as follows:

object SQLConnection1: TSQLConnection


DriverName = Datasnap
LoginPrompt = False
Params.Strings = (
DriverUnit=DBXDataSnap
HostName=localhost
Port=211
CommunicationProtocol=tcp/ip
DatasnapContext=datasnap/
DriverAssemblyLoader=Borland.Data.TDBXClientDriverLoader,Borland +
.Data.DbxClientDriver,Version=15.0.0.0,Culture=neutral,PublicKey +
Token=91d62ebb5b0d1b1b
Filters={})
Connected = True
end

Although we can now right-click on the TSQLConnection component to produce a list of server methods by calling
the generate DataSnap client classes menu option, we can use a more powerful technique to work with the exposed
datasets from the ServerMethodsUnit (where we connected the Advantage Database Server components to a
TDataSetProvider for example, and also wrote a server method GetEvents to expose the events in a read-only way).

TSqlServerMethod
Lets start with the read-only list of events, as provided by the GetEvents server method. In order to call this server
method from the client, we need to place a TsqlServerMethod component next to the TSQLConnection and connect
the formers SQLConnection property to the latter. The next step involves selecting the name of the ServerMethod that
we want to call here, by opening the drop-down combobox for the ServerMethodName property. We need to pick the
TServerMethods1.GetEvents server method here:

This particular server method does not have any parameters, but otherwise we would have seen them in the
Params collection, which now only lists the ReturnParameter (containing the TDataSet with the events).
The TsqlServerMethod component needs to be connected to a local TDataSetProvider followed by a TClientDataSet
and a TDataSource if we want to place the data somewhere on the form (for example in a read-only TDBGrid). The
chain of components in this case is TSQLConnection TsqlServerMethod TDataSetProvider TClientDataSet and
TDataSource, followed by the data-aware controls such as TDBGrid, TDBNavigator and/or TDBEdit.

10

For people whove been using Delphi for some time, may notice the new TaqlServerMethod component, which is
part of the new DataSnap framework. The properties for our example are as follows:
object SqlServerMethod1: TSqlServerMethod
GetMetadata = False
Params = <
item
DataType = ftDataSet
Name = ReturnParameter
ParamType = ptResult
Value = TDataSet
end>
SQLConnection = SQLConnection1
ServerMethodName = TServerMethods1.GetEvents
end

The TDataSetProviders DataSet property should be pointing to the TsqlServerMethod component. The TClientDataSets
ProviderName property should be pointing to the TDataSetProvider, and the TDataSources DataSet property should be
connected to the TClientDataSet. Finally, the DataSource property of the data-aware controls can be connected to the
TDataSource component, allowing the visual controls to display the data from the imported ADS table.

This will give an overview of all previous events in the database, for which users could register themselves. Note
that the DataSnap Client does not connect directly to the ADS tables by itself, but only to the DataSnap Server. The
client is turned into a thin (or smart) client, and can even be deployed as a stand-alone executable!
If you do not want to keep the connection open at design-time, but rather write one line of code to activate the
TClientDataSet and request the data from the DataSnap server when the application starts, all you need is the
following implementation of the FormCreate event:
procedure TForm1.FormCreate(Sender: TObject);
begin
cdsEvents.Open;
end;

That will ensure that the cdsEvents TClientDataSet is opened, which will make the dspEvents TDataSetProvider
activate the SqlServerMethod1 to call the GetEvents method at the DataSnap server side, opening the connection
using the TSQLConnection component.

11

TDSProviderConnection
While the read-only list of events offers useful information, we may want to see more information. Specifically, I
would like to see the actual people who registered for a given event. And I also want to be able to modify the data of
the registered users (for example to fix typos or other details).
This requires another new Delphi DataSnap component, the TDSProviderConnection. Like the TsqlServerMethod, the
TDSProviderConnection is connected to the TSQLConnection component. But this time, instead of selecting a server
method to execute (at the server), the TDSProviderConnection is used as a gateway for TClientDataSet components
who want to connect to an exported TDataSetProvider from the DataSnap Server.
The TDSProviderConnection properties for our example are as follows:
object DSProviderConnection1: TDSProviderConnection
ServerClassName = TServerMethods1
Connected = True
SQLConnection = SQLConnection1
end

The only tricky part here is the specification of the ServerClassName, which is a string (so its possible to make
typing mistakes here). Other than that, we only need to connect to the TSQLConnection component and the
TDSProviderConnections Connected property will just pass on the value of the TSQLConnections Connected property.
For our example, we want to connect to the dspEventRegistration TDataSetProvider, exported from the DataSnap
Server. In order to do that, place a TClientDataSet component on the form, set its name to cdsEventRegistrations, set
the RemoteServer property to DSProviderConnection1 and the ProviderName property to dspEventRegistrations. This
will allow the TClientDataSet to retrieve all data from the exported TDataSetProvider.
In order to see what fields we get including the nested detail field double-click on the cdsEventRegistration
TClientDataSet to start the Fields Editor, and right-click in the Fields Editor and select Add All Fields to generate the
persistent fields that will contain all selected fields from the Events table as well as the Registrations for that event (in
a nested dataset):

The aqRegistrations field here is of type TDataSetField. We actually have to use a second TClientDataSet to work
with the individual records from the aqRegistration dataset. In order to do that, place a second TClientDataSet on
the form, call it cdsRegistrations, and point its DataSetField property to cdsEventRegistrationsaqRegistrations (which
is the combination of the cdsEventRegistrations TClientDataSet name and the aqRegistrations field inside that
TClientDataSet).
12

We also need two TDataSources now, one for each TClientDataSet, and a second TDBGrid to display the detail
Registration records that belong to a selected event from the master table.

There is one final step needed to make sure we can actually change and modify the data from these tables: sending
updates back to the server. These updates are not limited to just updates (changes within a record) but can also
include inserts (new records) as well as deletes (removal of record) of course.
The important detail here is that we have two TClientDataSets: a master cdsEventRegistrations and the detail
cdsRegistrations. In order to apply the updates back to the server, we need the master to call the ApplyUpdates
method. Even if the changes were only made in the detail TClientDataSet.
So, in order to implement this functionality in our example application, place a TButton on the form, set the caption
to Apply Updates (as can be seen in the previous screenshot already) and implement the OnClick event handler as
follows:
procedure TForm1.btnApplyUpdatesClick(Sender: TObject);
begin
cdsEventRegistrations.ApplyUpdates(0)
end;

Just a single line of code that will send all possible updates, inserts and deletes and send them over to the server
where they will be applied on the tables in the Advantage Database Server.
In case updates fail for example if a record was already removed, or changed by another user we can write
some code in the OnReconcileError event handler of the cdsEventRegistrations TClientDataSet. This falls beyond the
scope of this article, but see my DataSnap courseware manual for more details on DataSnap error handling.

13

DataSnap
In this article, Ive described and demonstrated how we can use Advantage Database Server and the Advantage
data access components for Delphi to work together with the DataSnap architecture in Delphi XE to build multi-tier
applications. The Application Server tier is using Advantage specific data access components, while the thin/smart
clients are database independent (and do not even need to know they are connected to an Advantage Database Server).
All further capabilities and benefits of DataSnap, including filters for compression and encryption, TCP/IP as well as
HTTP(S) transport protocols to connect Servers and Clients, and Authentication plus Authorization can be used as well
to extend the DataSnap example application. The bottom line, however, is that DataSnap is not limited to the data
access technologies like dbExpress (DBX4) and dbGo for ADO found in Delphi itself, but can also be used with the
Advantage Database Server and the Advantage data access components for Delphi.
So apart from moving from the BDE to ADS, we can go all the way and produce a multi-tier application with ADS as
the foundation.
Bob Swart Training & Consultancy Bob Swart (aka Dr.Bob www.drbob42.com)

References
Moving from the BDE to ADS Bob Swart www.drbob42.com/examines/examinC4.htm
Delphi XE DataSnap Development Bob Swart (also available from Lulu.com) www.eBob42.com/courseware

About the Author


Bob Swart (aka Dr.Bob) is an IT consultant, developer, reseller, author, trainer and webmaster for his company Bob
Swart Training & Consultancy (eBob42) based in Helmond, The Netherlands. Bob has spoken at Delphi Conferences
since 1993. Bob is co-author of several books and contributing author for numerous computer magazines. His
courseware manuals are sold worldwide from Lulu.com or via his own website www.eBob42.com/courseware.
Bob is reseller for Delphi, Delphi Prism and RAD Studio from Embarcadero in EMEA, as well as reseller for Advantage
Database Server from Sybase, RemObjects SDK and Hydra from Software Software, and TMS IntraWeb components
from TMS Software.

Contact us:
North America
Advantageinfo@Sybase.com
1 800 235 7576
Germany
ADS-team@Sybase.com
+49 (0) 7032 / 798 - 200
United Kingdom
ADS-team@Sybase.com
+44 (0) 117 315 3957

Sybase, Inc.
Worldwide Headquarters
One Sybase Drive
Dublin, CA 94568-7902
U.S.A
1 800 8 sybase

www.sybase.com

Copyright 2011 Sybase, Inc. All rights reserved. Unpublished rights reserved under U.S. copyright laws. Sybase, the
Sybase logo, Advantage Database Server and DataArchitect are trademarks of Sybase, Inc. or its subsidiaries. indicates
registration in the United States of America. SAP and the SAP logo are the trademarks or registered trademarks of SAP
AG in Germany and in several other countries. All other trademarks are the property of their respective owners. 06/11

You might also like