Server Frequently Asked Questions
- How can I convert a VCL RODA Server to a DLL Server?
- How can I integrate several DataAbstract modules into one server application?
- How can I migrate from a DA3 Server?
- "Undefined field found in data stream" exception at the OS X and iOS client applications.
How can I convert a VCL RODA Server to a DLL Server?
I have a standard RODA Server application that works great. Now I want to create a new version for a single user that has a simple install and simple usage.
Step 1
: Create a new RO DLL Server and add it to the current project group. Right click on the project group and select Add New Project ...
In the ensuing dialog, select the Remoting SDK for Delphi
tab and DLL Server
from options. Leave all the default options but do not create a test client (check box lower left). You'll get a new project that only has a project source available.
Step 2
: With the new project created, add all the files from the original server with the exception of the projects main form - we don't want forms in our DLL.
If you have built a standard RODA server, you'll have a datamodule that gets loaded before your servers main form. We need to emulate this in the DLL. To do this, change your standard DLL project code like this:
library EBHD;
{#ROGEN:MyLibrary.rodl} // RemObjects: Careful, do not remove! }
uses
uROComInit,
Windows,
uRODLLServer,
uROBinMessage,
{ My added unit files };
{$R *.RES}
{$R RODLFile.RES} // RemObjects: Careful, do not remove!
procedure ROProc(Reason:integer);
begin
case Reason of
DLL_PROCESS_ATTACH: begin
DMServer := TDMServer.Create(nil);
RegisterMessage(DMServer.ROMessage);
end;
DLL_PROCESS_DETACH: begin
DMServer.Free;
end;
end
end;
begin
DLLProc:=@ROProc;
ROProc(DLL_PROCESS_ATTACH);
end.
Note that the var section including the global BinMessage variable is removed, as is the instantiation and freeing of BinServer in the DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH areas. Now add the instantiation and freeing of the datamodule, instead (the datamodule variable DMServer is declared as a global variable in the datamodule itself).
If you check your standard server datamodule, you'll see that it already contains a server component and a message component. Our DLL server has it's own server component, called DLLServer, created in the uRODLLServer unit (check the initialization section of the unit). In other words, the standard server component is superfluous to requirements. That said, we only want to maintain one set of source code here. Time for some conditional compilation.
Step 3
: Add a {$DEFINE DLL}
statement at the top of the datamodule and then add some conditional statements. The idea is that we'll instantiate the standard ROServer in the standard datamodule only if the datamodule will not be used in a DLL. Here's an example code:
type
TDMServer = class(TDataModule)
DADriverMgr: TDADriverManager;
ROMessage: TROBinMessage;
InMemSessMgr: TROInMemorySessionManager;
DAIBODriver1: TDAIBODriver;
DAConnectionMgr: TDAConnectionManager;
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
public
{$IFNDEF DLL}
ROServer: TROIndyHTTPServer;
{$ENDIF}
end;
...
procedure TDMServer.DataModuleCreate(Sender: TObject);
{$IFNDEF DLL}
var
ROMD: TROMessageDispatcher;
begin
ROServer := TROIndyHTTPServer.Create( Self );
ROMD := ROServer.Dispatchers.Add as TROMessageDispatcher;
ROMD.Message := ROMessage;
ROServer.Active := True;
{$ENDIF}
end;
The declaration and instantiation of the server is simple enough, but note the declaration, instantiation and assignment of the TROMessageDispatcher.
In essence, that's it. To test your new DLL Server, select Run -> Parameters
from the Delphi main menu. Declare the client app that you'll use to test the DLL, set your breakpoints in the DLL and then run from within the IDE.
How can I integrate several DataAbstract modules into one server application?
-
Create a RO SDK service and several additional DLL projects (your DA modules) in the same solution. Each DLL module should hold its own RODL and schema, but use the common connection definition file. There is no wizard to create such DA modules, so each module should be created manually. Don't forget to set BuildAction property to EmbededResource on RODL and daSchema files.
-
Now these DA modules can be loaded by demand using the
Assembly.LoadFile()
method inside your SDK server. As a result, different RODL will be merged and exposed out as a single RODL (this looks very similar to Modular server sample from RO SDK for .NET).
- Configuring the Client. Since several services are used, several RemoteDataAdapters should be used that point to different remote services. Or client should switch between remote services before loading or updating data.
Note: Since data is obtained from different services, Strongly Typed DataSets will be generated separately for each service. If they should be combined into single Dataset, this should be done manually.
How can I migrate from a DA3 Server?
To begin with, remove the old DataAbstract3 and add DataAbstract4.RODL. You can do it in Service Builder by Edit->Use Existing RODL->DataAbstract4. We have changed some types, so you need to adjust them if you are using them in your server methods:
DA3 type | DA10 type |
---|---|
TDADatasetParam |
DataParameter |
TDADatasetRequestInfo |
TableRequestInfo |
TDALoginInfo |
UserInfo |
TDADatasetParamArray |
DataParameterArray |
TDADatasetRequestInfoArray |
TableRequestInfoArray |
TDAStringArray |
StringArray |
TDALoginService |
TSimpleLoginService or TMultiDbLoginService |
TDARemoteService |
TDataAbstractService |
The easiest way to update your _impl.pas is to backup your old _impl.pas, remove it from the project and recreate it with Regenerate Units from RODL
(IDE
->Tools
->Remoting SDK
menu).
After this, you can open your old _impl.pas in Notepad and move code into the new _impl.pas.
A more complicated way, which could be preferable if you use a lot of components in your _impl, is to open it in your notepad and change the ancestor from TDARemoteService
to TDataAbstractService. You also need to add DataAbstract4_Intf and DataAbstractService_Impl into the uses section. Now you can open it in the IDE and manually update code.
Tip: You can open the _intf file and copy/paste the method declaration into _impl.
"Undefined field found in data stream" exception at the OS X and iOS client applications.
If you are trying to obtain table data from the custom Data Abstract method for the OS X or iOS client, then you have to make sure that this method serializes the schema of the table with its data.
// DataAbstract for Delphi
dataStreamer.WriteDataset(dataSet, [woRows, woSchema], -1);
// Data Abstract for .NET
Boolean includeSchema = true;
this.ServiceDataStreamer.WriteDataReader(reader, table, -1, includeSchema);
It is important because in most cases, OS X & iOS clients know nothing about table structure and they just recreate table instance according to the info from stream. If table schema was not serialized along with its data then you can get an exception during reading from the stream.
Undefined field %fieldName% found in data stream.
Which happens because it cannot match the data in the stream with table which does not have columns.