We use the dependency injection framework from Spring4D and have an issue with the way it constructs objects if it cannot resolve all constructor parameters. In our case all parameters of the constructor of the ancestor can be resolved and it looks like that Spring4D now uses this constructor. Even if we reintroduce
the constructor the descedant, it still uses the constructor of the ancestor.
However, the behavior I would expect (and prefer) is that the resolving fails if it cannot find a correct constructor for the interface you try to resolve.
Example:
TMyClass = class(TInterfacedObject, IMyClass)
constructor Create(ARequester: IRequester);
end;
TMyDescendant = class(TMyClass, IMyDescendant)
constructor Create(ARequester: IRequester; AnotherRequester: IOtherRequester);
end;
GlobalContainer.Resolve<IMyDescendant>
In this example it will call the constructor of TMyClass
if there is no class registered that implements IOtherRequester
. This means that any Guard.IsNotNull
that are in the constructor of TMyDescendant
doesn't work, because that constructor is never called.
Are there ways to resolve this? Of course we could override the constructor of TMyClass
in TMyDescendant
, but I prefer a more clean way to do this.
CodePudding user response:
There are multiple solutions to this problem.
There is a container extension for that in the unit
Spring.Container.ActivatorExtension
. You simple have to register it like so:GlobalContainer.AddExtension<TActivatorContainerExtension>;
This changes the behavior of the constructor discovery for the entire container to only consider the constructors of the most derived class that has any constructors and thus avoids going back to
TObject.Create
if any dependencies are missing.Keep in mind that even though you could have written overload on your two param constructor and not having registered
IOtherRequester
it would thus not go to theTMyClass
constructor which it could satisfy given that you registered someIRequester
. This is because RTTI does not include any information about overloads and even if it would that would mean the container then would have to implement overload resolution which can get quite complicated.Annotate your constructor with
[Inject]
(remember to add the unitSpring.Container.Common
to the uses or it will not have any effect - depending on the compiler version you then will see the W1074 warning)This will force to container to only consider this ctor and ignore any other. You can in fact annotate both constructors with it - it will always find the most derived one first. If you would inherit other classes from
TMyClass
that don't introduce their own constructors it will then force the one fromTMyClass
to be used.