delphicontainersspring4d

Spring4d - Constructor with interface and object as parameters


For example I have 3 frames like this:

TBaseFrame = class(TFrame)
end;

TfraDataAwareEntity = class(TBaseFrame )
  private
    FEntity: TObject;
    procedure SetEntity(const Value: TObject);
    { Private declarations }
  public
    constructor Create(AOwner: TComponent;
                       ADataset: IDataset;
                       AEntity: TObject); 
    property Entity: TObject read FEntity write SetEntity;
  end;

TfraDataAwareEntityWithGrid = class(TBaseFrame )
  private
    FEntity: TObject;
    procedure SetEntity(const Value: TObject);
    { Private declarations }
  public
    constructor Create(AOwner: TComponent;
                       ADataset: IDataset;
                       AEntity: TObject); 
    property Entity: TObject read FEntity write SetEntity;
  end;

Interface:

IDataset = interface
  ['{065771AA-A114-4D01-B180-427A39DD51D7}']
     procedure Save;
     procedure Load;
end;

Implementation:

TCustomDataset = class(TInterfacedObject, IDataset)
    procedure Save;
    procedure Load;
end;

Registration:

 GlobalContainer.RegisterType<TBaseFrame , TfraDataAwareEntity >
    ('TfraDataAwareEntity ');
 GlobalContainer.RegisterType<TBaseFrame , TfraDataAwareEntityWithGrid >
    ('TfraDataAwareEntityWithGrid ');
 GlobalContainer.RegisterType<TCustomDataset >.Implements<IDataset>.AsDefault;

Now I want to create a frame:

 GlobalContainer.Resolve<TBaseFrame>('TfraDataAwareEntity ',
      [ Self,
        GlobalContainer.Resolve<IDataset>,
        SomeObj ]);

The problem with the code above, it doesn't accept an Interface as parameter (error: There is no overloaded version of 'TContainer.Resolve<FrameUnit.TfraDataAwareEntity >' that can be called with these arguments), I need to pass TCustomDataset but if I have a factory that returns an IDataSet I cant use it, and I don't want the unit to be coupled with TCustomDataset, just IDataSet. So How I go about it?

Another option I tried would be inject TCustomDataset on TfraDataAwareEntity constructor (like this example: "Unsatisfied constructor" on constructor injection with Spring4D )but then I get an error that cant resolve TComponent, is there a way to inject TCustomDataset in IDataset in the constructor and also pass the rest of parameters?


Solution

  • You need to wrap the values into TValue.From(...) (you can omit the specific type in <..> because type inference will take care of it) because

    I also suggest you register this as factory function in your container to not have the code you posted in your consumer code - that would not be dependency injection but rather the anti pattern of service locator because you gained nothing but traded constructor calls with resolve calls.

    The missing code to the one you posted:

    type
      TFrameFactory = reference to function(const AFrameName: string; AOwner: TComponent; AEntity: TObject): TBaseFrame;
    
    
      GlobalContainer.RegisterInstance<TFrameFactory>(
        function(const AFrameName: string; AOwner: TComponent; AEntity: TObject): TBaseFrame
        begin
          Result := GlobalContainer.Resolve<TBaseFrame>(AFrameName,
            [TValue.From(AOwner), TValue.From(GlobalContainer.Resolve<IDataset>), TValue.From(AEntity)]);
        end);
    

    You can then get TFrameFactory injected into the component where you want to be able to construct your frames.

    Note about future plans for Spring: