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.
{ 3 } Comments
Wouldn’t be helpful to also have a basic TDBXDateTimeValue?
It may seem that TDBXTimeStampValue is enough, but the issue is that an implicit cast of a TimeStamp into a database field type of DATE (basically a DateTime), basically kills the usage of any index on those fields.
Further details in
http://qc.embarcadero.com/wc/qcmain.aspx?d=34745
Maybe there’s a workaround, but I just haven’t found it yet.
I don’t know if TDBXDateTimeValue is needed to fix this problem. However, to make sure that R&D is aware of the performance/index issue, I’ve added a comment to the internal bug report associated with QC 34745.
I’m also not sure if it will be absolutely required to have it, but it just *seems* to have it’s root on the lack of such basic DBXValue type (TDBXDateTimeValue). I’m quite probably way out on this assumption… but sometimes all it takes is a different perspective to solve something
Anyway, thank you for rising this with R&D.
{ 1 } Trackback
[...] Jim Tierney from Embarcadero has already discussed different DataSnap parameter types on his blog and during recent CodeRage5 conference session "DataSnap: Features and Integration with User [...]
Post a Comment