#1 2022-01-23 23:19:39

talexone
Member
Registered: 2013-08-21
Posts: 21

Dependency injection in interface based service

How to properly declare interfaces to permit a dependency injection in interface based services?

UserService.pas

  IUserService = interface(IInvokable)
    ['{A8C05236-F397-4422-9201-E676AB82F380}']
      function Read(ID: TID; out User: TSQLUser): boolean;   
  end;

  TUserService = class(TInterfacedObject, IUserService)
    private
      fUserRepository: IUserRepository;
    public
      constructor Create(aUserRepository: IUserRepository); reintroduce;
      function Read(ID: TID; out User: TSQLUser): boolean;                              
  end;

initialization

TInterfaceFactory.RegisterInterfaces([
   TypeInfo(IUserService)
   ]);  

UserRepository.pas

  IUserRepository = interface(IInvokable)
    ['{D4AF2ABB-5D53-43CA-8403-EC4A65BB76C4}']
    function Read(ID: TID; out User: TSQLUser): boolean;
  end;

  TUserRepository = class(TInterfacedObject, IUserRepository)
  private
    fRest: TSQLRest;
  public
    constructor Create(aRest: TSQLRest); reintroduce;
    function Read(ID: TID; out User: TSQLUser): boolean;
  end;

initialization

TInterfaceFactory.RegisterInterfaces([
   TypeInfo(IUserRepository)
   ]); 

Main.pas

...
aServer := TSQLRestServerDB.Create(lModel, SQLITE_MEMORY_DATABASE_NAME);
aServer.ServiceDefine(TUserService, [IUserService], sicShared);
...

The reintroduced Create constructors never called and the Read function raising exception 'fRepository is nil' . Only works a manual injection: aServer.ServiceDefine(TUserService.Create(TUserRepository.Create(aServer), [IUserService]), but I think it's not the right solution.

Offline

#2 2022-01-24 02:09:14

uian2000
Member
Registered: 2014-05-06
Posts: 68

Re: Dependency injection in interface based service

This constructor should be override, not reintroduced.
Otherwise, you should create instance by hand, then register it using overloaded version of ServiceDefine(aSharedImplementation, [aInterfaces])

Edit 2022 01 24 1556
Wired DI features are implemented in REST part of the code, not SOA part.

regards

Last edited by uian2000 (2022-01-24 07:58:34)

Offline

#3 2022-01-24 04:23:31

Chaa
Member
Registered: 2011-03-26
Posts: 244

Re: Dependency injection in interface based service

See TInjectableObjectRest.

Offline

#4 2022-01-25 00:07:52

talexone
Member
Registered: 2013-08-21
Posts: 21

Re: Dependency injection in interface based service

uian2000 wrote:

This constructor should be override, not reintroduced.

One can override only the same ancestor method. If the method was declared with a different parameters it can be only overload or reintroduce.

Chaa wrote:

See TInjectableObjectRest.

Switching to TInjectableObjectRest seems to work

 TUserService = class(TInterfacedObjectRest, IUserService)
 ...

constructor TUserService.Create;
begin
  inherited Create;
  Assert(Server.Services.Resolve(IUserRepository, fUserRepository),'IUserRepository object not found'); 
end; 

But what I'm looking for is automated DI

Offline

#5 2022-01-25 01:24:57

uian2000
Member
Registered: 2014-05-06
Posts: 68

Re: Dependency injection in interface based service

talexone wrote:

One can override only the same ancestor method. If the method was declared with a different parameters it can be only overload or reintroduce.

You are right.

Chaa wrote:

See TInjectableObjectRest.

This is quite the answser.
More over, you can inherite your service from TInjectableObjectRest, then override the constructor  CreateWithResolverAndRest.
This way DI automaticly processed when you define interface implementation.

Regards.

Offline

#6 2022-01-25 22:48:26

talexone
Member
Registered: 2013-08-21
Posts: 21

Re: Dependency injection in interface based service

TInjectableObjectRest works if declared with ServiceDefine, but this way a useless root/interfacename/{method} entry points were created. I tried TInterfaceResolverInjected.RegisterGlobal so dependable classes can find and create the object, but Rest is not injected anymore in created object (Create called instead of CreateWithResolverAndRest).


TUserService.UserRepository created and TUserService.UserRepository.Server injected, but root/userrepository/{method} service's entry points declared.

TUserRepository = class(TInjectableObjectRest, IUserRepository) 
...
TUserService = class(TInjectableObject, IUserService) 
    private
      fUserRepository: IUserRepository;
    published
      property UserRepository: IUserRepository read fUserRepository write fUserRepository;  
...
    aServer.ServiceDefine(TUserRepository, [IUserRepository], sicShared);
    aServer.ServiceDefine(TUserService, [IUserService], sicShared);
 

TUserService.UserRepository created, but TUserService.UserRepository.Server is nil

  TInterfaceResolverInjected.RegisterGlobal(TypeInfo(IUserRepository),TUserRepository);
  aServer.ServiceDefine(TUserService, [IUserService], sicShared);

How to register a TInjectableObjectRest class with a proper auto Rest injection and avoiding a useless entry points creation?

Offline

Board footer

Powered by FluxBB