Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

delphi - how to pass a parameter from the instantiator to a constructor in the spring4d dependency injection framework?

It's possible to register a class with a parameter expected to be passed from the point of creation?

I know it can be done something like this:

GlobalContainer.RegisterType<TUserProcessor>.Implements<IUserUpgrader>.
AsTransient.DelegateTo(
    function: TUserProcessor
    begin
      Result := TUserProcessor.Create(GetCurrentUser);
    end
  );

But there the parameters are binded to the execution context where the container gets registered and not where the object get's intantiated.

Something like this it's possible for example?

GlobalContainer.Resolve<IMathService>([FCurrentUser]);

I know some peoble advocate to have very simple constructors, but there are times when a constructor parameter looks clearly the way to go:

  1. The object constructed needs the object parameter to work, so the reference must be satisfied. The parameter also makes that constraint much more obvious looking at the class.

  2. You can assign the reference in a method or property and raise and exception in every other method if you try to use the object without first making the assignment.. I don't like writing this type of code it's simply a waste of time, just use the constructor parameter and check there. Less code, the better IMO.

  3. Also the object being passed it's local to the object that constructs the new object using the container (for example a Transaction object) and has some state (it's not a new object that I can get with the container).

like image 476
pragmatic_programmer Avatar asked Mar 31 '12 01:03

pragmatic_programmer


Video Answer


1 Answers

I have added resolver overrides as in Unity.

So you can write:

program Demo;

{$APPTYPE CONSOLE}

uses
  Classes,
  Spring.Container,
  Spring.Container.Resolvers;

type
  IUserUpgrader = interface
    ['{ADC36759-6E40-417D-B6F7-5DCADF8B9C07}']
  end;

  TUser = class(TObject);

  TUserProcessor = class(TInterfacedObject, IUserUpgrader)
  public
    constructor Create(AUser: TUser);
  end;

constructor TUserProcessor.Create(AUser: TUser);
begin
  Writeln('called constructor with passed user');
end;

begin
  GlobalContainer.RegisterType<TUserProcessor>.Implements<IUserUpgrader>;
  GlobalContainer.Build;

  GlobalContainer.Resolve<IUserUpgrader>(
    TOrderedParametersOverride.Create([TUser.Create]));

  GlobalContainer.Resolve<IUserUpgrader>(
    TParameterOverride.Create('AUser', TUser.Create));

  Readln;
end.

Edit 17.09.2018:

This changed quite some while ago and Resolve is accepting parameters via array of TValue. You can either pass values directly there which then have to match exactly the parameter list in the ctor. For only partially filling the parameters you can use TNamedValue or TTypedValue from Spring.pas

Anyway I suggest using factories to be injected and consumed by your code and not reaching into the container (global one or not) Resolve method.

like image 166
Stefan Glienke Avatar answered Nov 16 '22 03:11

Stefan Glienke