Codefirst server - accessing Service Method from Server

delphi
(Stuart Clennett) #1

Hi,

My custom delphi code-first RO/DA server needs to poll a REST end point and update the database with the contents (if changed).

I am implementing this by using a TTimer on the fServerDataModule. In the OnTimer event I am polling the end-point (in a TTask) to get a JSON string.

Then I am creating an instance of my data-service :slight_smile:

//FRules is a string containing JSON from REST end-point
ClientID := ROMessage.ClientID;
fClassFactory := GetClassFactory('AquilaService');
fClassFactory.CreateInstance(ClientID, instance);
IAquilaService(instance).UpdateRulesFromJSON(fRules);  //<-- need client access

In order to access the UpdateRulesFromJson method, I have to include my client-access interface unit (AquilaLibrary_Intf).

When I do this and compile the server, each time I try to access a data table on the client I get an exception "Error reading parameter aTableRequestInfoArray: Unexpected class found in stream; class “TableRequestInfo” does not descend from “TableRequestInfo”.

When I remove the reference to AquilaLibrary_Intf from the fServerDataModule it all works fine, but then I cannot utilise a reference to IAquilaService or any of its methods.

What is the best way to implement this scenario?

Thanks

0 Likes

(EvgenyK) #2

Code-First server-side: you shouldn’t use client-side _Intf here.
if you want, you can declare personal interface for this service on server-side

I can suggest to use methods of uDALocalHelpers.pas for receiving IDataAbstractLocalServiceAccess that can be cast later to your own interface.

it is better that your code because it calls OnActivate/OnDeactivate that can be needed if your service require sessions.

0 Likes

(Stuart Clennett) #3

Thanks Evgeny,

Any chance of a code sample to get me started? I’m at all familiar with uDALocalHelpers.pas

Thanks

0 Likes

(EvgenyK) #4
  fServiceInstance := LocalServiceAccessHelper_Acquire(SessionID, ServiceName);
..
  LocalServiceAccessHelper_Release(SessionID, ServiceName, fServiceInstance);

You can review code in uDALocalDataAdapter.pas or uDALocalCommand.pas.
see getter/setter of ServiceInstance property

0 Likes

(Stuart Clennett) #5

Thanks, Evgeny.

One last thing - the service requires a session. How is best to get a ClientID from the ServerDataModule ?

I tried using the DADBSessionManager1.CreateSession() & DeleteSession() but still gave me a SessionNotFoundException

Do I need to get an instance of my LoginService (using the above code??) and then Login(…) and Logout…?

Thanks

0 Likes

(EvgenyK) #6

you can create session as

  function intCreateSession: TGUID;
  var
    lSession: TROSession;
  begin
    Result := NewGuid;
    lSession := ServerDataModule.SessionManager.CreateSession(Result);
    CheckNotNull(lSession,'Session is null!');
    ServerDataModule.SessionManager.ReleaseSession(lSession, true);
  end;

ofc, it will work if you don’t have extra initialization in Login method

0 Likes

(Stuart Clennett) #7

Hi,

Thanks, but this doesn’t seem to be working for me:

      clientID := NewGuid;
      lSession := db_SessionManager.CreateSession(clientID);
      try
        // session not found on the line below still.
        fAquilaService := LocalServiceAccessHelper_Acquire(clientID, 'AquilaService');  
        try
          IAquilaService(fAquilaService).UpdateRulesFromJSON(fRules);
        finally
          LocalServiceAccessHelper_Release(clientID, 'AquilaService', fAquilaService);
        end;
      finally
        db_SessionManager.ReleaseSession(lSession, true);
      end;

It still gives me a Session not found error on the fAquilaService := LocalServiceAccessHelper.... line

The lSession object is valid, but in debugging I see no record created in my database’s session manager table (not sure if it should).

I do have some code in the SimpleLoginService.OnLogin event handler – but it’s only to check password hash, initialise the UserInfo object that is returned to the client and some logging of user activity - not something I would need on the server.

Thanks

0 Likes

(EvgenyK) #8

use it as

      clientID := NewGuid;
      lSession := db_SessionManager.CreateSession(clientID);
      db_SessionManager.ReleaseSession(lSession, true);

      fAquilaService := LocalServiceAccessHelper_Acquire(clientID, 'AquilaService');  
      try
        IAquilaService(fAquilaService).UpdateRulesFromJSON(fRules);
      finally
        LocalServiceAccessHelper_Release(clientID, 'AquilaService', fAquilaService);
      end;
1 Like

(Stuart Clennett) #9

Thanks, I clearly thought releaseSession was like releasing memory === destroying :slight_smile:

One more thing - I’m still unclear as to how to use the fServiceInstance?

Do I create another interface declaration for the Service so that I can use it’s methods?

I tried declaring this inside the ServerDataModule:

IAquilaService = interface(IROService)
['{800A03A9-0952-4C11-B273-DF54995DD9CA}']
  procedure UpdateRulesFromJSON(const aRulesJson: string);
end;

and then casting fServiceInstance :slight_smile:

IAquilaService(fServiceInstance).UpdateRulesFromJson(aRules);

but this just gave me access violations so I guess it’s not the right way.

Many thanks

0 Likes

(EvgenyK) #10

you can’t it cast in this way.

better way -

if Supports(fServiceInstance, IAquilaService , as1) then 
   as1.UpdateRulesFromJson(aRules)
else
  raise Exception.Create('service does not support IAquilaService');
0 Likes

(Stuart Clennett) #11

Thanks.

I’m using the same code but the Supports() function is returning false, so I don’t get my interface reference ?

if Supports(fAquilaService, IAquilaService, Svc) then   //<-- false
  Svc.UpdateRulesFromJSON(fRules);

The interface is defined in the fServerDataModule unit (for now) as:

IAquilaService = interface(IROService)
['{CD0C5940-FC27-4150-9C2D-42A8D1B7416A}']
  procedure UpdateRulesFromJSON(const aRulesJson: string);
end;

There is a lot more methods than that one - do I need to copy the entire interface definition from the client unit ?

Thanks

0 Likes

(EvgenyK) #12

have you added IAquilaService into declaration of your service, like

  TDataService = class(TDataAbstractService, IAquilaService )

?

if you needed that all - yes, otherwise only what you are need.

1 Like

(Stuart Clennett) #13

D’oh !

Yes, that was the problem. Everything working as it should now.

Many thanks, you’ve been really helpful.

Stuart

0 Likes