CodeRage 4 Samples
I’ve uploaded the sample DataSnap projects used in my CodeRage 4 sessions.
Samples from "DataSnap Tooling" (Wednesday, September 9 at 7 pm PDT) are available here: http://cc.embarcadero.com/Item/27221.
| Projects | Descriptions | Screen shots |
|---|---|---|
| EchoStringServer | Simple console style DataSnap server with TCP/IP and HTTP support. | EchoStringServer.jpg |
| DelphiEchoStringClient | Simple client to call the "EchoString" DataSnap server method. May be used with EchoStringServer or with your own server created with one of the DataSnap server wizards.. | DelphiEchoStringClient.jpg |
| SampleProxyGenerator | Standalone VCL application to generate a Datasnap client proxy in Delphi or C++. Generates code just like the proxy generator integrated with TSQLConnection. | SampleProxyGenerator.jpg |
| PrismEchoStringClient | Simple client to call the "EchoString" DataSnap server method. Works like DelphiEchoStringClient except using DBX/ADO.NET rather than native DBX. | PrismEchoStringClient.jpg |
| SamplePrismProxyGenerator | Standalone Winform application to generate a DataSnap client proxy in Prism. Generates code just like the proxy generator integrated with the Prism Server Explorer. | SamplePrismProxyGenerator.jpg |
Samples from "DataSnap Server Method Table Parameters" (Thursday, September 10 at 6 pm) are available here: http://cc.embarcadero.com/Item/27222.
| Projects | Descriptions | Screen shots |
|---|---|---|
| DataSnapTablesSampleServer | DataSnap server with server methods declared with different types and directions of table parameters (TDataSet, TDBXReader, TParams, out, var, return and in). | DataSnapTablesSampleServer.jpg |
| DataSnapTablesListViewClient | DataSnap client that can call a server method to get a table then display the table in a ListView. The server method must pass a table back to the client using var or out parameters, or result. Use with DataSnapTablesSampleServer. | DataSnapTablesListViewClient.jpg |
| DataSnapTablesLoggingServer | DataSnap server that logs activity during a server method call (e.g.; enter server method, read table, exit server method). | DataSnapTablesLoggingServer.jpg |
| DataSnapTablesLoggingClient | DataSnap client that logs activity during a server method call (e.g.; call server method, read table, return from server method call). Use with DataSnapTablesLoggingServer. | DataSnapTablesLoggingClient.jpg |
| DataSnapTablesCDSClient | DataSnap client with components to populate a TClientDataSet from a server method result table, and display in a grid. Components include TSQLConnection, TSQLServerMethod, TDataSetProvider, TClientDataSet, TDataSource and TDBGrid. | DataSnapTablesCDSClient.jpg |
| DataSnapTablesProxyClient | DataSnap client that calls some of the methods in DataSnapTablesLoggingServer using a client proxy class generated using the TSQLConnection "Generate DataSnap client classes" command. | DataSnapTablesProxyClient.jpg |
Share This | Email this page to a friend
Posted by Jim Tierney on September 8th, 2009 under DataSnap, Delphi, Delphi Prism, Oxygene | 3 Comments »DataSnap 2010 HTTP support with an ISAPI dll
With DataSnap in Delphi 2010, you can develop DataSnap clients and server that communicate over HTTP. One way to implement the server is as an ISAPI dll. I’ve been given permission to show this feature pre-release. See http://www.embarcadero.com/rad-studio-2010 for for more information about the upcoming release.
Click these links to skip ahead Configure IIS, Connect with Data Explorer, Ancestors and DataModules, ISAPI debugging
In the File/New… dialog, double click “DataSnap WebBroker Application”
Check “ISAPI/NSAPI Dynamic link Library”, “Add Server Methods Class” and “Include sample methods”.
Select TPersistent as the ancestor class for the server methods. I will say more about the ancestor choices later in this post.
Click OK to create a new project.
The new project has two units. ServerMethodsUnit implements the server methods for this DataSnap server. There is a single server method called EchoString with a string parameter (see my previous posts to learn more about server method parameter types).
The WebModule contains the components that make this ISAPI dll function as a DataSnap 2010 HTTP server.
DSServer1 and DSServerClass are the same components used in stand alone TCP/IP DataSnap servers. DSHTTPWebDispatcher is new to DataSnap 2010. It is responsible for managing ISAPI/WebBroker HTTP requests and responses on behalf of the DataSnap Server.
Both DSHTTPWebDispatcher1 and DSServerClass1 components have their “Server” property set to “DSServer1”. The DSServerClass1 component calls an event handler to get the type of the server methods class:
The wizard has generated a complete ISAPI application so no changes are required to the project source code, however I do need to configure IIS.
Here is how I configure IIS 6.0 on my Vista x32 system.
If you haven’t enabled IIS on your system then use “Programs and features”/”Turn Windows features on or off” to install.
Start Internet Information Services (IIS) Manager.
Expand Connections nodes, right click on “Default Web Site”, choose “Add Virtual Directory”.
Enter an alias of “DSProject” an a physical path of "c:\DSProject".
ISAPI must be enabled for this virtual directory. Select the “DSProjects” Connections node. Double click the “Handler Mappings” icon. Click Actions/“Edit Handler Permissions…”. Check “Execute”.
Directory browsing is convenient but not required. Enable it for this example. Select “DSProjects” Connections node. Double click the “Directory Browsing” icon. Click Actions/”Enable”.
Almost done configuring IIS. Click on the root Connection node. Double click the “ISAPI and CGI Restrictions” icon. Click Actions/”Edit Feature Setting…”. I have “Allow unspecified ISAPI modules” checked. If you don’t check this then you will need to add to the list of ISAPI dlls recognized by IIS, after you have built your project.
Now go back to Delphi 2010 and set the output directory to the virtual directory physical path c:\DSProject.
Build the project.
Browse to http://localhost/DSProject and, if you have enabled directory browsing, you should see the ISAPI dll. Click it. You should see a page like this:
The following a wizard generated line of code provide the default content:
At this point it looks like our ISAPI dll is properly configured. Now I can use the Data Explorer to test connectivity.
I prefer to use the stand alone DataExplore.exe when testing connections rather than the RAD studio Data Explorer view, just in case there is a connection problem that causes the client to become unresponsive.
Start DataExplorer.exe. Right click the DataSnap node, choose add connection. Enter “NewConnection”. Click OK.
Expand the DataSnap node, right click “New Connection” and select “Modify Connection”.
Set Protocol to “http”, Host to “localhost”, Port to “80” and Path to “DsProject/Project1.dll”.
Click “Test Connection”. Click OK. Click OK.
Expand the NewConnection/Procedures node to see that EchoString is a server method.
To test, right click on the “EchoString” node and choose "View Parameters". Select the "Value" parameter and enter some text for the "Value" property. Right click and choose "Execute" (or click the the image button in the upper left of the view parameters window).
At this point I’ve created a server, configured it to run under IIS, and connected to it with DataExplorer. The new DataSnap 2010 HTTP client features will need to be covered in another post.
I have two additional topics to cover. First is this ancestor issue I mentioned near the beginning of this post. Finally, I will add a few words about debugging.
This example used TPersistent as the ancestor of our server method class. The other choices, in the wizard, are TDataModule and TDSServerModule. Use TDataModule when you want to use components in your server methods. Use TDSServerModule when you want the equivalent of TRemoteDataModule.
When using TDataModule or TDSServerModule, there is an important requirement that only affects WebBroker applications. Be sure that the there is only one module created during startup. Use Project/View Source… to see this code. The only module created here should be the WebModule.
The project source should look like this:
Something like this will result in an exception with the message “Only one data module per application”.

In case your ISAPI dll isn’t working properly because of this or other issues, here is how I configure my project for debugging.
In project options, set Host application to the location of w3wp.exe and the Parameters to “-debug”.
You will need to stop IIS before debugging. Use the IIS manager or [c:/]net stop w3svc.
When you run the application, you should see a prompt like this:
When you stop debugging, w3wp.exe will be shut down. When you are done working on your project, restore IIS with [c:/]net start w3svc.
Share This | Email this page to a friend
Posted by Jim Tierney on August 20th, 2009 under Data Explorer, DataSnap, Delphi, WebBroker | 8 Comments »Delphi Prism and DataSnap Server Method Stream Parameters
This post is about using Delphi Prism to pass streams to/from DataSnap server methods. The sample client that goes along with this post is available here: http://cc.embarcadero.com/item/26874.
This client has the same functionality as the Delphi client described in this post: DataSnap Server Method Stream Parameters. Here is a screen shot of the two clients running against the same Delphi sample server.
Download the Delphi sample server (and Delphi sample client) here: http://cc.codegear.com/item/26854. For introductory information about the difference between Delphi and Delphi Prism DataSnap clients see this post: Call DataSnap Server Methods with Delphi Prism.
Stream Parameter Types
This table shows the parameter types and return types in the stream samples. The columns under “Server Parameter” describe the parameter or return type in the Delphi DataSnap servers methods. The “Delphi Type” column shows the corresponding type used in the Delphi client. The “Delphi Prism Type” column shows type used in the Delphi Prism client.
| Server Parameter | Client Parameters | |||||
|---|---|---|---|---|---|---|
| Type | (default) | var | out | Result | Delphi Type | Delphi Prism Type |
| TStream |
|
|
|
|
TStream | System.IO.Stream |
| TDBXStreamValue |
|
|
|
|
TStream | System.IO.Stream |
Code Samples
The following code samples show the differences between calling server methods with DbExpress in Delphi compared to ADO.NET in Delphi Prism.
| TStream Server Method |
|---|
function TTestStream.Echo(I: TStream): TStream; |
| Delphi/DbExpress |
function TTestStreamClient.Echo(I: TStream): TStream;
begin
if FEchoCommand = nil then
begin
FEchoCommand := FDBXConnection.CreateCommand;
FEchoCommand.CommandType := TDBXCommandTypes.DSServerMethod;
FEchoCommand.Text := 'TTestStream.Echo';
FEchoCommand.Prepare;
end;
FEchoCommand.Parameters[0].Value.SetStream(I, FInstanceOwner);
FEchoCommand.ExecuteUpdate;
Result := FEchoCommand.Parameters[1].Value.GetStream(FInstanceOwner);
end;
|
| Delphi Prism/ADO.NET |
function TTestStreamClient.Echo(I: System.IO.Stream): System.IO.Stream;
begin
if FEchoCommand = nil then
begin
FEchoCommand := FDBXConnection.CreateCommand as TAdoDbxCommand;
FEchoCommand.CommandType := System.Data.CommandType.StoredProcedure;
FEchoCommand.CommandText := 'TTestStream.Echo';
FEchoCommand.Prepare;
end;
FEchoCommand.Parameters[0].Value := I;
FEchoCommand.ExecuteNonQuery;
Result := FEchoCommand.Parameters[1].Value as System.IO.Stream;
end;
|
| TDBXStreamValue Server Method |
|---|
procedure TTestStreamValue.Swap(I: TDBXStreamValue; J: TDBXStreamValue); |
| Delphi/DbExpress |
procedure TTestStreamValueClient.Swap(var I: TStream; var J: TStream);
begin
if FSwapCommand = nil then
begin
FSwapCommand := FDBXConnection.CreateCommand;
FSwapCommand.CommandType := TDBXCommandTypes.DSServerMethod;
FSwapCommand.Text := 'TTestStreamValue.Swap';
FSwapCommand.Prepare;
end;
if I = nil then
FSwapCommand.Parameters[0].Value.SetNull
else
FSwapCommand.Parameters[0].Value.SetStream(I, FInstanceOwner);
if J = nil then
FSwapCommand.Parameters[1].Value.SetNull
else
FSwapCommand.Parameters[1].Value.SetStream(J, FInstanceOwner);
FSwapCommand.ExecuteUpdate;
if FSwapCommand.Parameters[0].Value.IsNull then
I := nil
else
I := FSwapCommand.Parameters[0].Value.GetStream(FInstanceOwner);
if FSwapCommand.Parameters[1].Value.IsNull then
J := nil
else
J := FSwapCommand.Parameters[1].Value.GetStream(FInstanceOwner);
end;
|
| Delphi Prism/ADO.NET |
procedure TTestStreamValueClient.Swap(var I: System.IO.Stream; var J: System.IO.Stream);
begin
if FSwapCommand = nil then
begin
FSwapCommand := FDBXConnection.CreateCommand as TAdoDbxCommand;
FSwapCommand.CommandType := System.Data.CommandType.StoredProcedure;
FSwapCommand.CommandText := 'TTestStreamValue.Swap';
FSwapCommand.Prepare;
end;
if I = nil then
FSwapCommand.Parameters[0].Value := DbNull.Value
else
FSwapCommand.Parameters[0].Value := I;
if J = nil then
FSwapCommand.Parameters[1].Value := DbNull.Value
else
FSwapCommand.Parameters[1].Value := J;
FSwapCommand.ExecuteNonQuery;
if FSwapCommand.Parameters[0].Value = DBNull.Value then
I := nil
else
I := FSwapCommand.Parameters[0].Value as System.IO.Stream;
if FSwapCommand.Parameters[1].Value = DBNull.Value then
J := nil
else
J := FSwapCommand.Parameters[1].Value as System.IO.Stream;
end;
|
That’s all for now. A post on table parameter types is coming soon.
Share This | Email this page to a friend
Posted by Jim Tierney on April 25th, 2009 under DataSnap, Delphi Prism, Oxygene | 3 Comments »DataSnap Server Method Stream Parameters
This is a continuation of my posts on DataSnap server method parameters and return types. This post is about TStream and TDBXStreamValue. The sample client and server projects that go with this post can be downloaded here: http://cc.embarcadero.com/item/26854
See my earlier posts on “Basic” and “Basic DBXValue” types. I’ve yet to post about TDBXConnection, TDBXConnectionValue, TDBXReader, TParams, and TDataSet. However, TDataSet is demonstrated in the sample projects.
| Basic | Basic DBXValue | Collection | Connection | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
|
The following table summarizes differences between TStream and TDBXStreamValue types:
| Supports null values | Declaration | Accessing Values | Proxy generator | ||||
|---|---|---|---|---|---|---|---|
| Default parameter direction | Other parameter directions | function result type | Get | Set | |||
| TStream | No |
in |
in/out: use var keyword out: use out keyword |
Yes |
lhs := AParameter | AParameter := rhs |
Yes |
| TDBXStreamValue |
Yes |
in/out |
None |
No |
AParameter.IsNull lhs:=AParameter.GetStream(InstanceOwner) | AParameter.SetNull AParameter.SetStream(rhs, InstanceOwner) |
No |
Supports null values
The TDBXStreamValue type has an IsNull property and a SetNull method. Use this type instead of TStream a parameter value can be null/nil, in some cases.
Declaration
The var and out keywords can’t be used to specify the parameter direction of a TDBXStreamValue parameter. The direction is always in/out. A TDBXStreamValue type can’t be used as a function result.
Proxy Generator
The RAD Studio 2007 client proxy generator does not work properly for server methods with TDBXStreamValue parameters. So you will need to hand code the client code or correct the generated proxy. The sample client has hand corrected proxy methods that look like this.
function TTestStreamValueClient.Echo(var I: TStream): TStream;
begin
if FEchoCommand = nil then
begin
FEchoCommand := FDBXConnection.CreateCommand;
FEchoCommand.CommandType := TDBXCommandTypes.DSServerMethod;
FEchoCommand.Text := 'TTestStreamValue.Echo';
FEchoCommand.Prepare;
end;
if I = nil then
FEchoCommand.Parameters[0].Value.SetNull
else
FEchoCommand.Parameters[0].Value.SetStream(I, FInstanceOwner);
FEchoCommand.ExecuteUpdate;
if FEchoCommand.Parameters[0].Value.IsNull then
I := nil
else
I := FEchoCommand.Parameters[0].Value.GetStream(FInstanceOwner);
Result := FEchoCommand.Parameters[1].Value.GetStream(FInstanceOwner);
end;
This client method works with a server method declared as follows:
function Echo(I: TDBXStreamValue): TStream;
Calling a server method with a TDBXStreamValue parameter is the same as calling a server method with an TStream parameter, except that you can use SetNull and IsNull methods to work with null values.
For example, compare the following server method declaration and client method implementation to the previous example:
function Echo(I: TStream): TStream;
function TTestStreamClient.Echo(I: TStream): TStream;
begin
if FEchoCommand = nil then
begin
FEchoCommand := FDBXConnection.CreateCommand;
FEchoCommand.CommandType := TDBXCommandTypes.DSServerMethod;
FEchoCommand.Text := 'TTestStream.Echo';
FEchoCommand.Prepare;
end;
FEchoCommand.Parameters[0].Value.SetStream(I, FInstanceOwner);
FEchoCommand.ExecuteUpdate;
Result := FEchoCommand.Parameters[1].Value.GetStream(FInstanceOwner);
end;
Accessing Values
The GetStream and SetStream methods have an InstanceOwner parameter. Passing True indicates that DBX owns the stream and will free it. Passing False indicates that the caller owns the stream. To control how the generated proxy classes call SetStream and GetStream, there is an AInstanceOwner parameter on the proxy class constructor:
constructor TTestStreamClient.Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean);
The other constructor is equivalent to passing AInstanceOwner as True.
constructor TTestStreamClient.Create(ADBXConnection: TDBXConnection);
Sample Client And Server Applications
The sample server has a few simple server methods for passing values (including null).
{$METHODINFO ON}
TTestCollection = class(TComponent)
strict protected
procedure LogMessage(const AMessage: string);
public
// Server method to get the parameter type names
procedure GetTypeNames(I: T; ADeclaredName, AActualName: TDBXStringValue);
end;
{$METHODINFO OFF}
TTestStream = class(TTestCollection)
public
function Echo(I: TStream): TStream;
procedure Copy(I: TStream; out J: TStream);
procedure Swap(var I: TStream; var J: TStream);
end;
TTestStreamValue = class(TTestCollection)
public
function Echo(I: TDBXStreamValue): TStream;
procedure Copy(I: TDBXStreamValue; J: TDBXStreamValue);
procedure Swap(I: TDBXStreamValue; J: TDBXStreamValue);
function IsNull(I: TDBXStreamValue): Boolean;
procedure SetNull(I: TDBXStreamValue);
end;
The sample client tests the server methods by calling them with sample values and verifying the results (e.g; comparing streams). TMemoryStream is used to pass short streams and TFileStream to pass long streams. Here is a screen shot of the running server and client:
The following table shows the parameter types and return types demonstrated in the sample client and server:
| Type | Direction | |||
|---|---|---|---|---|
| (default) | var | out | Result | |
| TDBXStreamValue | X (in/out) | |||
| TStream | X (in) | X | X | X |
Stream Usage
Check “Log Stream Usage” to show TStream sizes, type names, creates, destroys, and reads. The results from 4 different test cases are displayed after “Call Server Methods” is clicked.
The screen shot below shows the result from a test case with longer streams. I’ve added highlighting to illustrate these points:
- When the server reads a longer stream passed from the client, round trips are made to the client. This makes it possible to send very large streams to the server without consuming lots of memory.
- When the client reads a longer stream passed from the server, round trips are made to the server. This makes it possible to return very large streams to the client without consuming lots of memory.
- The TStream objects that represent longer client and server streams have a Size of –1, meaning the size is unknown. Rely on the return value of TStream.Read to detect the end of the stream. Also note that some DBX streams such as TDBXStreamReaderStream do not fully support Seek.
That’s all for now.
Share This | Email this page to a friend
Posted by Jim Tierney on April 6th, 2009 under DataSnap, Delphi | Comment now »Call DataSnap Server Methods with Delphi Prism
This post provides information about calling DataSnap server methods from Delphi Prism clients.
There are two new samples that go along with this post: http://cc.embarcadero.com/item/26806 and http://cc.embarcadero.com/item/26805
The first sample is a Delphi Prism client that demonstrates "Basic" types. This client has nearly the same functionality as the Delphi client described in this post: DataSnap Server Methods Parameters. The difference is that the Delphi Prism client isn’t able to call server methods with OleVariant parameter types. The Delphi client is shown on the left and the Delphi Prism client on the right:
The second sample is a Delphi Prism client that demonstrates "Basic DBXValue" types. This client has the same functionality as the Delphi client described in this post: DataSnap Server Method DBXValue Parameters. The Delphi client is shown on the left and the Delphi Prism client on the right:
The Delphi Prism clients use the same sample servers as the Delphi clients. Download the Delphi sample servers (and Delphi sample clients) here: http://cc.codegear.com/item/26702, http://cc.embarcadero.com/item/26734.
The following table summarizes the major differences between the Delphi and Delphi Prism sample clients:
| Clients | Connectivity | Proxy Generator | UI Framework | Parameter Types | Language/RTL | |
|---|---|---|---|---|---|---|
| Framework | Types | |||||
| Delphi | DbExpress |
TDBXConnection, TDBXCommand |
Yes |
VCL |
Delphi |
Delphi/Delphi |
| Delphi Prism | ADO.NET |
TAdoDbxConnection, TAdoDbxCommand |
No |
WinForms |
Oxygene/.NET |
Oxygene/.NET |
Connectivity
The Delphi Prism client uses the DataSnap ADO.NET provider rather than native DbExpress. Here are some code samples to show differences between calling DataSnap server methods with DbExpress in Delphi vs. ADO.NET in Delphi Prism.
| Delphi |
|---|
TTestIntegerClient = class
private
FDBXConnection: TDBXConnection;
FInstanceOwner: Boolean;
FEchoCommand: TDBXCommand;
FCopyCommand: TDBXCommand;
FSwapCommand: TDBXCommand;
public
constructor Create(ADBXConnection: TDBXConnection); overload;
constructor Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean); overload;
destructor Destroy; override;
function Echo(I: Integer): Integer;
procedure Copy(I: Integer; out J: Integer);
procedure Swap(var I: Integer; var J: Integer);
end;
|
| Delphi Prism |
TTestIntegerClient = class
private
FDBXConnection: TAdoDbxConnection;
FInstanceOwner: Boolean;
FEchoCommand: TAdoDbxCommand;
FCopyCommand: TAdoDbxCommand;
FSwapCommand: TAdoDbxCommand;
protected
procedure Dispose(ADisposing: Boolean); virtual;
public
constructor Create(ADBXConnection: TAdoDbxConnection);
constructor Create(ADBXConnection: TAdoDbxConnection; AInstanceOwner: Boolean);
procedure Dispose; virtual;
function Echo(I: Integer): Integer;
procedure Copy(I: Integer; out J: Integer);
procedure Swap(var I: Integer; var J: Integer);
end;
|
| Delphi |
procedure TTestIntegerClient.Swap(var I: Integer; var J: Integer);
begin
if FSwapCommand = nil then
begin
FSwapCommand := FDBXConnection.CreateCommand;
FSwapCommand.CommandType := TDBXCommandTypes.DSServerMethod;
FSwapCommand.Text := 'TTestInteger.Swap';
FSwapCommand.Prepare;
end;
FSwapCommand.Parameters[0].Value.SetInt32(I);
FSwapCommand.Parameters[1].Value.SetInt32(J);
FSwapCommand.ExecuteUpdate;
I := FSwapCommand.Parameters[0].Value.GetInt32;
J := FSwapCommand.Parameters[1].Value.GetInt32;
end;
|
| Delphi Prism |
procedure TTestIntegerClient.Swap(var I: Integer; var J: Integer);
begin
if FSwapCommand = nil then
begin
FSwapCommand := FDBXConnection.CreateCommand as TAdoDbxCommand;
FSwapCommand.CommandType := System.Data.CommandType.StoredProcedure;
FSwapCommand.CommandText := 'TTestInteger.Swap';
FSwapCommand.Prepare;
end;
FSwapCommand.Parameters[0].Value := I;
FSwapCommand.Parameters[1].Value := J;
FSwapCommand.ExecuteNonQuery;
I := FSwapCommand.Parameters[0].Value as Integer;
J := FSwapCommand.Parameters[1].Value as Integer;
end;
|
Proxy Generator
Delphi Prism does not have a DataSnap client proxy generator like the one in Delphi. The preceding Delphi code sample was generated with the proxy generator; the Delphi Prism code sample was hand written.
UI Framework
The Delphi Prism sample clients are WinForm applications. DataSnap clients may be implemented in other application types supported by Delphi Prism, such as ASP.NET.
Parameter Types
The following table shows the parameter types and return types demonstrated in the “Basic” types samples. The columns under “Server Parameter” describe the parameter or return type in the Delphi DataSnap server’s methods. The “Delphi Type” column shows the corresponding type used in the Delphi client. The “Delphi Prism Type” show the corresponding type used in the Delphi Prism client.
| Server Parameter | Client Parameters | |||||
|---|---|---|---|---|---|---|
| Type | (in) | var | out | Result | Delphi Type | Delphi Prism Type |
| AnsiString | X | X | AnsiString | String | ||
| Boolean | X | X | X | X | Boolean | Boolean |
| Currency | X | X | X | X | Currency | Decimal |
| TDateTime | X | X | X | X | TDateTime | DateTime |
| TDBXDate | X | X | X | X | TDBXDate | DateTime |
| TDBXTime | X | X | X | X | TDBXTime | DateTime |
| Double | X | X | X | X | Double | Double |
| Int64 | X | X | X | X | Int64 | Int64 |
| Integer | X | X | X | X | Integer | Integer |
| LongInt | X | X | X | X | LongInt | LongInt |
| OleVariant | X | X | X | OleVariant | <Not supported> | |
| Single | X | X | X | X | Single | Single |
| SmallInt | X | X | X | X | SmallInt | SmallInt |
| String | X | X | String | String | ||
| WideString | X | X | WideString | String | ||
The following table shows the parameter types and return types demonstrated in the “Basic DBXValue” types samples:
| Server Parameter | Client Parameters | |||||
|---|---|---|---|---|---|---|
| Type | (in/out) | var | out | Result | Delphi Type | Delphi Prism Type |
| TDBXAnsiCharsValue | X | AnsiString | string | |||
| TDBXAnsiStringValue | X | AnsiString | string | |||
| TDBXBcdValue | X | TBcd | Decimal | |||
| TDBXBooleanValue | X | Boolean | Boolean | |||
| TDBXDateValue | X | TDBXDate | DateTime | |||
| TDBXDoubleValue | X | Double | Double | |||
| TDBXInt16Value | X | Int16 | Int16 | |||
| TDBXInt32Value | X | Int32 | Int32 | |||
| TDBXInt64Value | X | Int64 | Int64 | |||
| TDBXSingleValue | X | Single | Single | |||
| TDBXStringValue | X | String | String | |||
| TDBXTimeValue | X | TDBXTime | DateTime | |||
| TDBXTimeStampValue | X | TSQLTimeStamp | DateTime | |||
| TDBXWideCharsValue | X | UnicodeString | string | |||
| TDBXWideStringValue | X | UnicodeString | string | |||
Language Compatability
Syntax
Delphi Prism projects uses the Oxygene language.
In these samples, there are a few syntax differences between Oxygene .pas files and Delphi .pas files including keyword (e.g.; unit vs. namespace, var and out in method calls) and a delimiter (e.g.; Oxygene requires ‘;’ and ‘@’ in places that Delphi doesn’t). Diff the like named .pas files for details.
Delphi Prism has some project options that enable the Oxygene compiler to accept more Delphi language conventions. Choose “Project/Project properties…” The one I’ve checked for these samples is “Allow ‘Create’ constructor calls’. This option enables compilation of the following line:
LTest := TTestBooleanClient.Create(DBXConnection)
Otherwise, the code would need new instead of Create:
LTest := new TTestBooleanClient.(DBXConnection).
Destroy vs. Dispose
In the Delphi Prism samples, Dispose is called in place of Destroy in the Delphi samples. Calling Dispose doesn’t free the object but tells the object to release unmanaged resources (like a tcp/ip connection).
SysUtils.Format vs. String.Format
In the Delphi Prism samples, String.Format is called in place of Format in the Delphi samples:
LogTestMessage(string.Format(’{0:s} {1:s}’, [FClassName + '.' + LMethodName, LDeclaredType]));
in place of
LogTestMessage(Format(’%0:s (%1:s)’, [FClassName + '.' + LMethodName, LDeclaredType]));
That’s all for now. I (continue to) plan on covering collection types in my next post.
Share This | Email this page to a friend
Posted by Jim Tierney on March 25th, 2009 under DataSnap, Delphi Prism, Oxygene | 3 Comments »DataSnap Server Method DBXValue Parameters
This post provides more information about DataSnap server method parameters and return types. This is my second post on this subject, following DataSnap Server Method Parameters. The previous post was about "Basic" types. This post is about "Basic DBXValue" types. The sample client and client and server projects that go with this post can be downloaded here: http://cc.embarcadero.com/item/26734
| Basic | Basic DBXValue | Collection | Connection | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
|
The following table summarizes differences between "Basic" and "Basic DBXValue" types:
| Supports null values | Declaration | Accessing Values | Proxy generator | ||||
|---|---|---|---|---|---|---|---|
| Default parameter direction | Other parameter directions | function result type | Get | Set | |||
| Basic | No |
in |
in/out: use var keyword |
Yes |
lhs := AParameter | AParameter := rhs |
Yes |
| DBXValue |
Yes |
in/out |
None |
No |
AParameter.IsNull lhs:=AParameter.GetInt32, lhs:=AParameter.GetString, etc. |
AParameter.SetNull AParameter.SetInt32(rhs), AParameter.SetString(rhs), etc. |
No |
Supports null values
Support for null values is needed, for example, when a server method needs to pass values that come from a database column that allows NULL. All DBXValue types have an IsNull property and a SetNull method.
Declaration
The var and out keywords can’t be used to specify the parameter direction of a DBXValue parameter. The direction is always in/out. A DBXValue type can’t be used as a function result.
Proxy Generator
The RAD Studio 2007 client proxy generator does not work properly for server methods with DBXValue parameters. So you will need to hand code. The sample client has code like this to call a server method:
// Non-generic version of TestSetNull
procedure TCallTestMethods.TestSetNullInt32(AConnection: TDBXConnection; I: Int32);
const
LMethodName = 'TTestDBXInt32Value.SetNull';
var
LIsNull: Boolean;
LDBXCommand: TDBXCommand;
begin
LDBXCommand := AConnection.CreateCommand;
try
try
LDBXCommand.CommandType := TDBXCommandTypes.DSServerMethod;
LDBXCommand.Text := LMethodName;
LDBXCommand.Prepare;
LDBXCommand.Parameters[0].Value.SetInt32(I);
LDBXCommand.ExecuteUpdate;
LIsNull := LDBXCommand.Parameters[0].Value.IsNull;
LogTestResult(LMethodName, LIsNull);
except
on E: Exception do
LogTestException(LMethodName, E);
end;
finally
LDBXCommand.Free;
end;
end;
Calling a server method with a TDBXInt32Value parameter is the same as calling a server method with an Int32 parameter, except that you can use SetNull and IsNull methods to work with null values.
Accessing Values
The following table shows the methods that are used to access a non-null value from the different DBXValue types A mismatched call, such as GetBcd on TDBXStringValue, will raise an exception.
| Get method | Set method | Type | Use with |
|---|---|---|---|
| GetAnsiString | SetAnsiString | AnsiString | TDBXAnsiCharsValue, TDBXAnsiStringValue |
| GetBcd | SetBcd | TBcd | TDBXBcdValue |
| GetBoolean | SetBoolean | Boolean | TDBXBooleanValue |
| GetDate | SetDate | TDBXDate | TDBXDateValue |
| GetDouble | SetDouble | Double | TDBXDoubleValue |
| GetInt16 | SetInt16 | Int16 | TDBXInt16Value |
| GetInt32 | SetInt32 | Int32 | TDBXInt32Value |
| GetInt64 | SetInt64 | Int64 | TDBXInt64Value |
| GetSingle | SetSingle | Single | TDBXSingleValue |
| SetString | GetString | string | TDBXStringValue, TDBXWideCharsValue, TDBXWideStringValue |
| GetTime | SetTime | TDBXTime | TDBXTimeValue |
| GetTimeStamp | SetTimeStamp | TSQLTimeStamp | TDBXTimeStampValue |
| GetWideString | SetWideString | UnicodeString (same as string) | TDBXStringValue, TDBXWideCharsValue, TDBXWideStringValue |
String Value Types
Of the five DBXValue types that represent strings, you need only use TDBXAnsCharsValue to pass AnsiString values and TDBXWideCharsValue to pass UnicodeString/string values. The following table shows that these two types are always passed to server methods even if the method is declared with one of the other string types.
| Declared Type | Actual Type |
|---|---|
| TDBXAnsiCharsValue | TDBXAnsiCharsValue |
| TDBXAnsiStringValue | TDBXAnsiCharsValue |
| TDBXStringValue | TDBXWideCharsValue |
| TDBXWideCharsValue | TDBXWideCharsValue |
| TDBXWideStringValue | TDBXWideCharsValue |
Sample Client And Server Applications
The sample server has a few simple server methods for passing values (including null), and two informational server methods. In order to support various DBXValue types, I’ve implemented a generic server method base class:
{$METHODINFO ON}
TTestBasicDBXValueType = class(TComponent)
strict protected
function EqualValues(I: TValue; J: TValue): Boolean; virtual;
function GetValue(I: T): TValue; virtual; abstract;
procedure SetValue(I: T; Value: TValue); virtual; abstract;
public
// Server methods to pass values (including null)
function IsEqual(I: T; J: T): Boolean;
function Echo(I: T): TValue;
procedure Swap(I: T; J: T);
function IsNull(I: T): Boolean;
procedure SetNull(I: T);
// Server method to get the DBXValue parameter type names
procedure GetTypeNames(I: T; ADeclaredName, AActualName: TDBXStringValue);
// Server method to try DBXValue methods like GetInt32 and SetInt32
function TryGetAndSet(I: T): TDataSet;
end;
{$METHODINFO OFF}
Then I’ve used TTestBasicDBXValueType to declare and implement server methods classes for a variety of DBXValue types, such as TDBXInt32Value, TTestDBXDateValue , etc.
TTestDBXInt32Value = class(TTestBasicDBXValueType)
strict protected
function GetValue(I: TDBXInt32Value): Int32; override;
procedure SetValue(I: TDBXInt32Value; Value: Int32); override;
end;
TTestDBXDateValue = class(TTestBasicDBXValueType)
strict protected
function GetValue(I: TDBXDateValue): TDBXDate; override;
procedure SetValue(I: TDBXDateValue; Value: TDBXDate); override;
end;
Consult the sample server to see how I’ve implemented the "plumbing" to make these classes callable by a DataSnap client. As in the “Basic” types sample server, the implementation is unconventional because the TDSServerClass component is not used.
The sample client tests the generated methods by calling them with sample values and verifying the results. Consult the sample for implementation details. Here is screen shot of the running server and client:

The following table shows the parameter types and return types demonstrated in the sample client and server:
| Type | (in/out) | var | out | Result |
|---|---|---|---|---|
| TDBXAnsiCharsValue | X | |||
| TDBXAnsiStringValue | X | |||
| TDBXBcdValue | X | |||
| TDBXBooleanValue | X | |||
| TDBXDateValue | X | |||
| TDBXDoubleValue | X | |||
| TDBXInt16Value | X | |||
| TDBXInt32Value | X | |||
| TDBXInt64Value | X | |||
| TDBXSingleValue | X | |||
| TDBXStringValue | X | |||
| TDBXTimeValue | X | |||
| TDBXTimeStampValue | X | |||
| TDBXWideCharsValue | X | |||
| TDBXWideStringValue | X |
That’s all for now. I plan on covering the collection types in my next post.
Share This | Email this page to a friend
Posted by Jim Tierney on March 12th, 2009 under DataSnap, Delphi | 4 Comments »DataSnap Server Methods Parameters
Delphi 2009 introduced support for DataSnap server methods. If you are not familiar with this feature, here are two articles that describe server methods: DataSnap 2009 Overview , Getting Started with Delphi DataSnap 2009.
DataSnap server methods support a variety of parameter and return types. The following list shows the types grouped into my own categories.
This post is about "Basic" types. The sample client and server projects that go with this post can be downloaded here: http://cc.codegear.com/item/26702
| Basic | DBXValue | Collection | Connection |
|---|---|---|---|
|
|
|
|
Getting Started with Delphi DataSnap 2009 uses the following function as an example of a server method:
function TDSServerModule1.Echo(s: string): string; begin Result := 'Delphi DataSnap 2009 is echoing ' + s + ' ....' + s; end;
The proxy generator generates the following code to call this method:
function TDSServerModule1Client.Echo(s: string): string;
begin
if FEchoCommand = nil then
begin
FEchoCommand := FDBXConnection.CreateCommand;
FEchoCommand.CommandType := TDBXCommandTypes.DSServerMethod;
FEchoCommand.Text := 'TDSServerModule1.Echo';
FEchoCommand.Prepare;
end;
FEchoCommand.Parameters[0].Value.SetWideString(s);
FEchoCommand.ExecuteUpdate;
Result := FEchoCommand.Parameters[1].Value.GetWideString;
end;
I wanted my sample server and client to work like the sample in the article, with simple server methods and generated proxy in the client. In addition, I wanted to support various types so started with this generic server method implementation:
{$METHODINFO ON}
TTestBasicType = class(TComponent)
function Echo(I: T): T;
end;
{$METHODINFO OFF}
function TTestBasicType.Echo(I: T): T;
begin
Result := I;
end;
The I used TTestBasicType used to declare and implement "Echo" methods for a variety of types, such as string, boolean, and double:
TTestString = class(TTestBasicType<string>)) end; TTestBoolean = class(TTestBasicType<Boolean>) end; TTestDouble = class(TTestBasicType<Double>)) end;
To test parameters directions, I expanded the generic class with var and out parmeters:
{$METHODINFO ON}
TTestBasicType = class(TComponent)
function Echo(I: T): T;
procedure Copy(I: T; out J: T);
procedure Swap(var I: T; var J: T);
end;
{$METHODINFO OFF}
Consult the sample server to see how I’ve implemented the "plumbing" to make these classes callable by a DataSnap client. The implementation is unconventional because the TDSServerClass component is not used.
The sample VCL client is built starting with a TSQLConnection component. After setting the port and host name, I right clicked on the TSQLConnection and selected "Generate DataSnap client classes" to generate a client proxy:
In the generated code, there is a "Client" class for every one of the server classes. For example, TTestBooleanClient calls the TTestBoolean class on the server:
TTestBooleanClient = class private FDBXConnection: TDBXConnection; FInstanceOwner: Boolean; FEchoCommand: TDBXCommand; FCopyCommand: TDBXCommand; FSwapCommand: TDBXCommand; public constructor Create(ADBXConnection: TDBXConnection);overload; constructor Create(ADBXConnection: TDBXConnection; AInstanceOwner: Boolean); overload; destructor Destroy; override; function Echo(I: Boolean): Boolean; procedure Copy(I: Boolean; out J: Boolean); procedure Swap(var I: Boolean; var J: Boolean); end;
Here is the implementation of TTestBooleanClient.Echo. Consult the sample code to see the complete proxy implementation.
function TTestBooleanClient.Echo(I: Boolean): Boolean; begin if FEchoCommand = nil then begin FEchoCommand := FDBXConnection.CreateCommand; FEchoCommand.CommandType := TDBXCommandTypes.DSServerMethod; FEchoCommand.Text := 'TTestBoolean.Echo'; FEchoCommand.Prepare; end; FEchoCommand.Parameters[0].Value.SetBoolean(I); FEchoCommand.ExecuteUpdate; Result := FEchoCommand.Parameters[1].Value.GetBoolean; end;
The sample client tests the generated methods by calling them with sample values and verifying the results. Consult the sample for implementation details.
Here is screen shot of the running server and client:
The following table shows the parameter types and return types demonstrated in the sample client and server:
| Type | (in) | var | out | Result |
|---|---|---|---|---|
| AnsiString | X | X | ||
| Boolean | X | X | X | X |
| Currency | X | X | X | X |
| TDateTime | X | X | X | X |
| DBXDate | X | X | X | X |
| DBXTime | X | X | X | X |
| Double | X | X | X | X |
| Int64 | X | X | X | X |
| Integer | X | X | X | X |
| LongInt | X | X | X | X |
| OleVariant | X | X | X | |
| Single | X | X | X | X |
| SmallInt | X | X | X | X |
| String | X | X | ||
| WideString | X | X |
There are some Delphi types that you might expect to see in this list such as Byte and Cardinal. DataSnap currently doesn’t support these two nor LongWord, ShortInt, Word, TSQLTimeStamp, and TBcd. Support for var and out strings is coming (a workaround/alternative is to use TDBXStringValue in place of var String and TDBXAnsiStringValue in place of var AnsiString).
Thats all for now. I plan to cover more types in the future.
Share This | Email this page to a friend
Posted by Jim Tierney on February 13th, 2009 under DataSnap, Delphi | 6 Comments »


















(in)




RSS Feed

Connect with Us