BusinessProcessorRules and Sessions and

Hi there,

first of all: is the feature request list somewhere available? I’ve found a request (#6529 from anno 2009) which somehow touches my problem.

On the server side from within the business helper class i’d like to call some functions of a specific service. To access this I try to cast the TDABusinessProcessor.Service (submitted via Sender parameter of the event) to the corresponding interface (defined in Server_Intf.pas) and throw an exception if it doesn’t match (so this event can only be called by the “right” service. I also use the session from Sender.Service.Session (so I assume #6529 is already implemented).

The problem i have is: how can I access from a Table_A_ServerRules.BeforeProcessChange any other table using strongly typed interfaces? In the demo there’s stuff like DetailByName( nme_* ) within the client-side logic. However I have to validate and possible manipulate Table A from the server side and I have to update some other tables, too.

Is this somehow possible or is there a “best practice” on manipulating table x, y and z from within the helper class of table a?

The concept of these business helper classes is really nice however the demos always show some “easy” validation and so on and I just can’t get any other examples.

Another question I have is: how are these FieldRules applied? Will they generally applied to all fields with the same name or is there any logic behind to disinguish between table_a.foo and table_b.foo?

Thanks in advance,
Peter

Edit: is there any way to relocate some methods out of the service to the business helper classes? My main problem is to capsulate some behaviour like palletizing to the business helper class “packet” (Pallets). Currently I have this functions within the *_Impl.pas of the service, however there will be many packages, which may or may not call functions of each other, e.g. the palletizing package would call a method to generate a valid serial (seperate package) and would also log the palletizing action in an event log (sepeate package, too).
At first I thought this would be a good use of this business helper classes - however there seems no way to access further data tables… So for now I’m a little bit stucked and not very confident in implementing this functions into the *_Impl-file of the service.

Concerning this: is it possible to define a function from within the *BusinessProcessorRules class which can be called from the client? So that something like (dtMemTablePallet as IAdvancedPalletTable).CreatePallet( productclass : string ) would be called from the client and executed on the server?

Or do I have to split this functionalities into different services to keep the code clean? I don’t know what the right approuch is.

Hello,

The problem i have is: how can I access from a Table_A_ServerRules.BeforeProcessChange any other
table using strongly typed interfaces?
Unfortunatelly, you can’t access to other tablses using strongly typed interfaces

Another question I have is: how are these FieldRules applied? Will they
generally applied to all fields with the same name or is there any logic
behind to disinguish between table_a.foo and table_b.foo?
Please read about it in wiki:
Documentation | RemObjects Software

is there any way to relocate some methods out of the service to the
business helper classes?
You can use corresponded events of the business helper classes for this purpose.

Concerning this: is it possible to define a function from within the *BusinessProcessorRules
class which can be called from the client? So that something like
(dtMemTablePallet as IAdvancedPalletTable).CreatePallet( productclass : string ) would be
called from the client and executed on the server?
You can’t call this function from client directly.

Or do I have to split this functionalities into different services to keep the code clean?
Yes, you can do it.

OK,

so I’m going to split some of the functionality within seperate services. Is it possible to make a call from service1 to a function of service2 and if yes, which connections will be used? (e.g. PalletService.CreatePallet() tries to call CommonStuff.GenerateSerialNumber() )

Concerning the “multi services approuch” should every service has it’s on schema or can one service source to the schema of another one?

Edit2: removed old example, using exmaple found at:
http://www.codenewsfast.com/cnf/article/0/waArticleBookmark.7260536

However I cant access Session-property (from within Service_BBB) if i’m doing it the described way.

How can I ensure that the invoked method from the other server can access the same session?

Edit3: If I set clientId manually by casting the interface to tdataabstractservice, I got an error within Service_BBB while accessing Session property ( “The session has not been initialized.” ), the problem however is that I have to set the ID manually because the submitted clientID is not used (Service always has “{00000000-0000-0000-0000-000000000000}”) What am I doing wrong? I don’t want to use self.SessionManager.FindSession( self.ClientID ) (which works fine) - i just want to access the Session property.

Hello,
You wrote:

so I’m going to split some of the functionality within seperate services. Is it possible to make a
call from service1 to a function of service2 and if yes, which connections will
be used? (e.g. PalletService.CreatePallet() tries to call CommonStuff.GenerateSerialNumber() )

The simple way is:

procedure PalletService.CreatePallet();
var
CommonStuff:ICommonStuff;
begin
CommonStuff := TCommonStuff.Create(nil);
CommonStuff.GenerateSerialNumber();
end;

But in this case the connections will be different.
To use the same connection you can save it in Session:

procedure PalletService.CreatePallet();
var
CommonStuff:ICommonStuff;
s:TCommonStuff;
begin
s := TCommonStuff.Create(nil);
//Save connection in a session
s.Session[‘Connection’] := Integer(Connection);
CommonStuff := s as ICommonStuff;
CommonStuff.GenerateSerial;
end;

procedure TCommonStuff.GenerateSerial();
begin
//Restore connection from then session
if (not VarIsNull(Session[‘Connection’])) then
Connection := IDAConnection(Integer(Session[‘Connection’]));


end;

Concerning the “multi services approuch” should every service has it’s on
schema or can one service source to the schema of another one?
You can use one global schema in ServerDataModule.

However I cant access Session-property (from within Service_BBB) if i’m doing it the described way.
Use method like this:
procedure PalletService.CreatePallet();
var
CommonStuff:ICommonStuff;
s:TCommonStuff;
begin
s := TCommonStuff.Create(nil);
//s.Session
CommonStuff := s as ICommonStuff;

end;

Hello again,

I’m sorry, but I’m just not getting this to work: after creating the service with the constructor I am not allowed to access the Session-property (“Session has not been inializied.”). The newly constructed instance has a new ClientID (and so no session connected with it).

I can easily change the clientid to the clientid of the caller - however there’s no (easy?) way to assign the session of the caller to the just created service…

I’m still using the “fall 2011” release of RODA (but the change-log shows no changes regarding this…).

Hello,
Please assign SessionManager property of this service with ServerDataModule.SessionManager.

My project currently has four services:

  • SimpleLoginService: ApplicationLogin (SessionRequired = false)
  • DataAbstractService: ApplicationData (SessionRequired = false)
  • DataAbstractService: UserData (SessionRequired = true)
  • DataAbstractService: UserPallets (SessionRequired = true)

ApplicationLogin, UsereData and UserPallets have assigned the SessionManager from ServiceDataModule and every service has it’s own TDABin2DataStreamer, so this is not the problem.

However a service object created from within an other service does not get a correct session nor client id. To create an Instance of the service I wrote the following public method (within ServiceDataMain):

function TMyServerDataModule.GetServiceUserData( daConnection : IDAConnection = nil ) : IUserData;
begin
Result := TUserData.Create( nil );
if Assigned( daConnection ) then
begin
(Result as TDataAbstractService).Connection := daConnection;
(Result as TDataAbstractService).Session[ ‘test’ ] := ‘aha’; // raises exception!!!
end;
end;

The clientID of the newly created service is always {00000000-0000-0000-0000-000000000000} and no session is assigned…

Edit: OK, problem occurs because TRORemoteDataModule.GetSession does not try to find session if RequiredSession is assigned and just raises an exception. If I change SessionRequired to false just before accessing the Session-property first time it will create one (even if the ClientID/SessionID points to an existing one).

Any way to modify this or do I have to call somehow DoOnActivate (and if so do I have to call a corresponding deactivate event, too?)

Hello,

However a service object created from within an other service does not get a correct
session nor client id. To create an Instance of the service I wrote the
following public method (within ServiceDataMain):

It occurs because you call it not inside a service (TMyServerDataModule is not RO service).
You can use the following construction:

function TMyServerDataModule.GetServiceUserData( daConnection : IDAConnection = nil ) : IUserData;
begin
Result := TUserData.Create( nil );
if Assigned( daConnection ) then
begin
(Result as TDataAbstractService).SessionManager := SessionManager;
(Result as TDataAbstractService).ClientID := NewGuid();//you can also use any guid here
(Result as TDataAbstractService).Connection := daConnection;
(Result as TDataAbstractService).Session[ ‘test’ ] := ‘aha’;

end;
end;