You are not logged in.
Hi everyone!
I have been playing a bit with interface-based services using a Delphi client. But I was always doing tests with sicShared (the default creation mode).
Now I am trying to stop "playing", and start to do actual work, so now I need an interface-based service with sicPerUser mode instead.
How can I work with this from the Delphi client? I am trying to do the same as with sicShared, but the result is always False:
//With sicShared works fine, with sicPerUser returns always false
if FClient.Services.GUID(IMyINterfaceCreatedWithSicPerUser).Get(LMyInterface) then begin
//Work with interface
end;
I have seen that the "Get" procedure returns True only if the creation mode is sicShared, sicClientDriven or sicSingle. With any other creation mode, it returns False. So, which is the correct way to work with this creation modes?
Best regards,
LoPiTaL
Offline
Did you change the sharing type on both the server and client side? Failing to do so causes an interface contract error!
I am a little confused too about the types of sharing. Maybe Arnaud can say which one I need (my guess is perUserGroup)
1) 1st client userA connects, starts a simulation interface
2) 2nd client userB connects, starts a simulation interface
3) Connection if UserA Is dropped unexpectedly (powerloss, cable out, clean logout, whatever)
4) UserA reconnects.
Upon step 2 U want user B to use the same simulation object instance as UserA
Upon step4 I want user A to reconnect to the same simulation object instance as the one that was created in step 1
Last edited by h.hasenack (2013-04-15 07:05:29)
Offline
@Hans
If I understand well, perhaps UserA and UserB may be part of the same group, then the instance be sicPerGroup, so that in step 2 user B will use the same simulation object as UserA.
At reconnection, it will use the same.
Or if ALL users have to share the same simulation interface, using sicShared may be the good option.
@ LoPiTal
I will take a look at the client side implementation.
In all cases, BOTH client and server side MUST share the same setting, as Hans stated. Otherwise there will be a contract mismatch.
So if you have any problem, please share some small .dpr code able to reproduce the issue.
Please take a look at the SAD pdf, about the possible instance life time.
There is also some tables in the pdf, which shows the possible use cases.
Feel free to give feedback about the current documentation - source is in the repository, in the .pro file, so you can even submit patches!
Offline
Hi all!
I have both client and server in sicPerUser. The error is not a contract mismatch.
The problem is when calling the Get function from the service factory, which always returns False.
I copy and paste here the code of that function, in the "mORMot.pas" file, so I can show you:
function TServiceFactoryClient.Get(out Obj): Boolean;
var O: TInterfacedObjectFake;
begin
result := false;
if Self=nil then
exit;
case fInstanceCreation of
sicShared:
O := TInterfacedObjectFake(fSharedInstance);
sicSingle, sicClientDriven:
O := TInterfacedObjectFake(CreateFakeInstance);
else exit;
end;
pointer(Obj) := @O.fVTable;
O._AddRef;
result := true;
end;
In it, it is clear that it will return an interface only when the instance creation is of type sicShared, sicSingle or sicClientDriven. Any other option will go to the "else" part of the case and return False.
My code to get the interface is as follows, as I stated in the first post:
//With sicShared works fine, with sicPerUser returns always false
if FClient.Services.GUID(IMyINterfaceCreatedWithSicPerUser).Get(LMyInterface) then begin
//Work with interface
end;
Note that if I change to sicShared (of course, in both client and server) it works fine, so the register of the interface is correctly done both, in the server and in the client.
So I am guessing that I should retrieve the interface to work with with another method, but it is not stated in the documentation nor I have found it around the code.
Best regards,
LoPiTaL
Last edited by LoPiTaL (2013-04-15 12:53:55)
Offline
Offline
Thank you! It works fine now.
Another question (I don't open a new thread since it is related).
When creating the perUser instance, the ServiceContext is still not initialized, so it is not possible to retrieve the context information at creation time.
This can be solved by initializing SErviceContext before creating the instance in the "TServiceFactoryServer.ExecuteMethod", moving the following lines:
ThreadServer := @ServiceContext;
//WR := TJSONSerializer.CreateOwnedStream; //This one is not related, and should be kept where it is
try
with ThreadServer^ do begin
Factory := self;
Session := @Ctxt;
end; // RunningThread is already set at thread initialization
to the beginning of the method, and the following to the end:
finally
with ThreadServer^ do begin
Factory := nil;
Session := nil;
end;
//WR.Free; //This one is not related, and should be kept where it is
end;
This can be done? Or there is any other consideration?
If so, then it may be usefull to have the context info at instance creation time, i.e. for initializing data for caching, and using it without the need of checking in all the interface methods if the data is nil or not.
Best regards,
LoPiTaL
Last edited by LoPiTaL (2013-04-16 17:38:33)
Offline
It won't work with option to execute in main thread, for instance.
So you should better use an initalization method and put nothing in the constructor or AfterCreate.
See sample 16.
Offline