A Multi-Client-ECO application example.

The application we are going to build can be used for capturing worktime and breaks during the worktime. The worktime can be captured with multiple clients and the server application provides a view of all the data captured.
[The client part]

You start with setting up a winform eco application. This application shall be the client. I named it WorkTimeClient.
You need to have the UML Classes in a package so that you can add them later on to the server application.
New File - ECO UML Package
Create the following model:

WorkTime Model

Worktime and breaktime are specialized classes of a timespan. One worktime can have several breaktimes.
The operation calcTotal gives back the timespan of a worktime reduced by the accumulated timespans of the breaktimes. The result of calctotal is set in the diff attribute of the worktime class. Make sure you alter the classes according to the following code snippets:

function timestartend.calcTotal(): Timespan;
begin
Result := Timespan(Self.endtime-Self.starttime);
end;
function worktime.calcTotal(): Timespan;
var
ib: IbreaktimeList;
i: Integer;
begin
ib := get_breaktime;
for i := 0 to ib.Count-1 do begin
Self.diff := Self.diff - ib.Item[i].calcTotal;
end;
end;function breaktime.get_diff: TimeSpan;
begin
// If you add user code here, please remove the [EcoAutoMaintained] attribute
// from the property declaration in the interface section
Result := Timespan(Self._endtime-Self._starttime);
end;function worktime.get_diff: TimeSpan;
begin
// If you add user code here, please remove the [EcoAutoMaintained] attribute
// from the property declaration in the interface section
Result := TimeSpan(Self._endtime - Self._starttime);
end;

Now compile the application, go to the ecospace designer and add the package to the ecospace. Put a PersistenceMapperXml component on the ecospace so that you can develop the client without need for the server part so far. Set the filename of the persistencemapper to data.xml or whatever suites you well.
Switch now to the winform and create a frontend similar to this:WorkTimeClient Frontend
Add two expression handles and Currency Manager Handles for the datagrids.
In order to have the last row of the datagrid selected put the following code in the datagrids Paint event:

procedure TWinForm.DataGrid1_Paint(sender: System.Object; e: System.Windows.Forms.PaintEventArgs);
begin
DataGrid1.BindingContext[dataGrid1.DataSource,
dataGrid1.DataMember].Position := ehWorkTime.GetList.Count;
cmhWorktime.Position := ehWorkTime.GetList.Count;
end;

There probably could be smarter places to put it – but I don’t know them ;-)
But please let me know in case you know.
For the single client test of the app you can just assign the Update Database EcoAction to the Update DB Button – whereas in the Multi Client setting you need the following code:

var
c: IChange;
begin
EcoSpace.PersistenceService.RetrieveChanges;
for c in EcoSpace.PersistenceService.GetChanges do
if c.IsDirectConflict then c.Action := ChangeActionKind.Discard else
c.Action := ChangeActionKind.Keep;
EcoSpace.PersistenceService.ApplyAllChanges;
end;

This code retrieves via the EcoSpace persistence service all persisted changes. It could in a multi client environment happen that somebody changed data that you already had been changing in your local ecospace cache. The persistence service gives you the ability to handle arising conflicts if that is the case. I our case (loging worktime) it should not happen though.
Nevertheless after you retrieved the changes you walk via for … in loop over them to check if you have a conflict or not. You have to set for every IChange interface in the GetChanges-list the action to be used when the ApplyAllChanges procedure is called.
If you finished the client this way the only thing left to enable it for client server is to change the persistencemapper. We need a PersistenceMapperClient component on our ECOSpace since this is not on the ECO Componet Palette by default. We have to add it as follows:

  • right click on the Tool Palette and choose “Installed .NET components” from the pop up menu.

Adding the peristence mapper client
Then drag it to the eco category.
Delete the PersistenceMapperXML client from the ecospace and drop the PersistenceMapperClient on it. Set the url property to “tcp://localhost:8000/TestServer1” – since we will have the server an the client running on our local machine before deployment.


Save the project and close it or leave it open if you want to have client and server in a project group. Since it won’t run as long as we have our server developed - up and running because the socket the peristencemapperclient tries to connect to won’t be registered.

[The server part]

Create a new ECO winform project. I named it WorkTimeServer.
Add the worktime classes package and a persistence mapper provider:

Adding the ECO persistence mapper provider

In our example we use Interbase with the BDP. So drop a PersistenceMapperBdp and a BdpConnection component on the PersistanceMapperProvider ECOSpace. Set up the PersistenceMapperBDP for Interbase and configure the connection of the BDPConnection according to your local environment. You just have to set up an empty database. The tables will be created later on via the persistence mapper provider.
Go to the ecospace (not the persistencemapperprovider but the one connected to the winform) and select the worktime package from the available packages list:

Adding the WorkTime calsses
Compile the project and choose the EcoSpaceType to be used for the persistence mapper provider. (Click on the surface of the persistence mapper provider and select it from the drop down list in the object inspector).
You can now click on the Generate Schema button at the bottom of the EcoPersistanceMapper Provider. The result should look as follows:

ECO createt DB Mapping
You now have to uncomment the code for remoting in the persistence mapper provider. Do not forget to uncomment the declaration of the procedure and to add the required implementation uses-statement as stated in the comments.

// Sample code for registering the provider as a server using
// binary formatting on a TCP channel.
// add the following to your implementation level uses-statement when uncommenting the code
// System.Runtime.Serialization.Formatters, System.Runtime.Remoting, System.Runtime.Remoting.Channels, System.Runtime.Remoting.Channels.Tcp;class procedure TEcoPersistenceMapperProvider.RegisterTcpServer(Port: integer);
var
provider: BinaryServerFormatterSinkProvider;
props: IDictionary;
chan: TcpChannel;
begin
provider := BinaryServerFormatterSinkProvider.Create;
provider.TypeFilterLevel := TypeFilterLevel.Full; // Needed for serializations
props := Hashtable.Create;
props['port'] := Port;
chan := TcpChannel.Create(props, nil, provider);
ChannelServices.RegisterChannel(chan);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(TEcoPersistenceMapperProvider),
'TestServer1',
WellKnownObjectMode.Singleton);
end;

To finish setting up the Persistence Mapper Provider set the attribute SyncActive to true. You can also increase the value of the attributes MaxOpenConnections and MaxPoolConnections if you have a lot of clients connecting to the server.
Now add a PersistanceMapperClient to the Ecospace. Set the url property to tcp://localhost:8000/TestServer1 or if you changed the RegisterTCPServer procedure to the values you supplied there.
We have to call the RegisterTCPServer component before the PersistenceMapperClient is initialized. To do so change the constructor of the EcoSpace as follows:

constructor TWorkTimeServerEcoSpace.Create;
begin
inherited Create;
EcoPersistenceMapperProvider.TEcoPersistenceMapperProvider.RegisterTcpServer(8000);
InitializeComponent;
// TODO: Add any constructor code here
end;

The server part is now already fully functional. To add some insight into the data delivered by our working clients we create the following frontend:
Server Frontend
In the timer tick event you code the Persistence Service code already known from the client:

procedure TWinForm.Timer1_Tick(sender: System.Object; e: System.EventArgs);
var
c: IChange;
begin
EcoSpace.PersistenceService.RetrieveChanges;
for c in EcoSpace.PersistenceService.GetChanges do
if c.IsDirectConflict then c.Action := ChangeActionKind.Discard else
c.Action := ChangeActionKind.Keep;
EcoSpace.PersistenceService.ApplyAllChanges;
end;

Set the timer enabled property to true. Save and start the project and detach it from Delphi.

Detach program from Delphi

Open the client project again. Start the client project detach it and start a second client. Change the Username in the second client and experiment. It should be working and it is very easy once achieved - ECO can make you really happy.
Please let me know if I missed some important steps for understanding how to do it or if I did something not best practice like. I will change the article then according to your feedback.

  1. The samplecode of the server and client can be found here.

 

Share this article!

Follow us!

Find more helpful articles: