Search code examples
delphispring4d

"Unsatisfied constructor" on constructor injection with Spring4D


I'm struggling with the constructor injection of Spring4D. In a certain class i want to inject a specific implementation (by name) of an interface into the constructor.

Look at this:

IListFactory = interface
    ['{40...29}']
    function GetList : IListOfSomething;
end;

ICiderPress = interface
    ['{50...10}']
    procedure Press;
end;

TAppleListFactory = class(TInterfacedObject, IListFactory)
    function GetList : IListOfSomething;
end;

TCiderPress = class(TInterfacedObject, ICiderPress)
private
    FListFactory : IListFactory;
public
    constructor Create(const ListFactory : IListFactory);

    procedure Press;
end;

implementation

function TCiderPress.Create(const ListFactory : IListFactory);
begin
    FListFactory := ListFactory;
end;

procedure TCiderPress.Press;
begin
    // Do somtihing with FListFactory
end;

initialization
    GlobalContainer.RegisterType<TAppleListFactory>.Implements<IListFactory>('apple');

    GlobalContainer.RegisterType<TCiderPress>.Implements<ICiderPress>;
end.

Now I get an instance of my press with the ServiceLocator:

CiderPress := ServiceLocator.GetService<ICiderPress>;
CiderPress.Press;

and it works fine.

Now I add a second ListFactory:

TOrangeListFactory = class(TInterfacedObject, IListFactory)
    function GetList : IListOfSomething;
end;

and add the registration

GlobalContainer.RegisterType<TOrangeListFactory>.Implements<IListFactory>('orange');

and change my cidre press class to

TCiderPress = class(TInterfacedObject, ICiderPress)
private
    FListFactory : IListFactory;
public
    [Inject]
    constructor Create([Inject('apple')]const ListFactory : IListFactory);

    procedure Press;
end;

The Problem is, that the ctor of TCiderPress is not called.

If I add

GlobalContainer.AddExtension<TActivatorContainerExtension>;

I get an EActivatorException: Unsatisfied contructor on type: TCiderPress

What's going wrong?

EDIT:

It works, if i delegate the construction like this:

GlobalContainer.RegisterType<TCiderPress>.Implements<ICiderPress>
    .DelegateTo(function : TCiderPress
        begin
            Result := TCiderPress.Create(ServiceLocator.GetService<IListFactory>('apple');
        end
    );

EDIT2:

I found my error! I had to include Spring.Container.Common in the interface uses clause.

I'm using Delphi XE3 and Spring4D 1.1.3.


Solution

  • This works for me:

    unit Unit2;
    
    interface
    
    uses
      Spring.Container,
      Spring.Container.Common;
    
    type
      IListOfSomething = interface
      ['{ACCEF350-5FDE-4D60-BAE0-17F029A669ED}']
      end;
    
    
      IListFactory = interface
      ['{039DE93A-1235-4D75-A8E2-7265765F6E90}']
        function GetList : IListOfSomething;
      end;
    
      ICiderPress = interface
      ['{64C4F565-BB8C-42C0-9584-4F4A21779F52}']
        procedure Press;
      end;
    
      TAppleListFactory = class(TInterfacedObject, IListFactory)
      public
        function GetList: IListOfSomething;
      end;
    
      TOrangeListFactory = class(TInterfacedObject, IListFactory)
      public
        function GetList: IListOfSomething;
      end;
    
      TCiderPress = class(TInterfacedObject, ICiderPress)
      private
        FListFactory: IListFactory;
      public
        [Inject]
        constructor Create([Inject('apple')] const ListFactory: IListFactory);
        procedure Press;
      end;
    
    implementation
    
    constructor TCiderPress.Create(const ListFactory: IListFactory);
    begin
      FListFactory := ListFactory;
    end;
    
    procedure TCiderPress.Press;
    begin
      WriteLn(TObject(FListFactory).ClassName);
    end;
    
    { TAppleListFactory }
    
    function TAppleListFactory.GetList: IListOfSomething;
    begin
      Result := nil;
    end;
    
    { TOrangeListFactory }
    
    function TOrangeListFactory.GetList: IListOfSomething;
    begin
      Result := nil;
    end;
    
    initialization
      GlobalContainer.RegisterType<TAppleListFactory>.Implements<IListFactory>('apple');
      GlobalContainer.RegisterType<TOrangeListFactory>.Implements<IListFactory>('orange');
      GlobalContainer.RegisterType<TCiderPress>.Implements<ICiderPress>;
      GlobalContainer.Build();
    end.
    

    and consuming like

    o := GlobalContainer.Resolve<ICiderPress>;
    o.Press();