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?
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:
IDataset
) it will be automatically resolved by the container which avoids explicit resolve calls to provide all parameters.