How to insert a field interface into an object
I want to add an interface to an object, but I cannot find the problem with the [Inject] attribute
What works.
unit uStorage;
interface
uses
uStorageInterface;
type
TStorageService = class (TInterfacedObject, IStorageService)
private
FPath: String;
procedure SetPath(const Value: String);
function GetPath: String;
public
property Path: String read GetPath write SetPath;
end;
implementation
{ TStorageService }
function TStorageService.GetPath: String;
begin
Result:= FPath;
end;
procedure TStorageService.SetPath(const Value: String);
begin
FPath := Value;
end;
unit uStorageInterface;
interface
type
IStorageService = interface
['{F1B4C339-BE8E-4182-A191-95266160FA6E}']
procedure SetPath(const Value: String);
function GetPath: String;
property Path: String read GetPath write SetPath;
end;
IStorageObject = interface
['{7B97B659-EDF3-4892-AFAB-985487660372}']
end;
implementation
end.
unit uObjects;
interface
uses
Vcl.StdCtrls,
System.Classes,
uStorageInterface,
Spring.Container,
Spring.Services,
Spring.Container.Common;
type
TMyButton= class (TButton, IStorageObject)
private
FStorage: IStorageService;
function GetStorage: IStorageService;
protected
procedure DoExit; override;
public
constructor Create(AOwner: TComponent); override;
end;
implementation
{ TMyButton }
constructor TMyButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FStorage:= ServiceLocator.GetService<IStorageService>;
end;
procedure TMyButton.DoExit;
begin
inherited;
if assigned(FStorage) then
begin
self.Caption:= FStorage.Path;
end;
end;
function TMyButton.GetStorage: IStorageService;
begin
Result:= FStorage;
end;
end.
unit Unit2;
interface
uses
System.SysUtils, System.Classes,
Vcl.Dialogs;
type
TDataModule2 = class(TDataModule)
procedure DataModuleCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
DataModule2: TDataModule2;
implementation
uses
uStorage,
uObjects,
uStorageInterface,
Spring.Services,
Spring.Container;
{%CLASSGROUP 'Vcl.Controls.TControl'}
{$R *.dfm}
procedure TDataModule2.DataModuleCreate(Sender: TObject);
begin
GlobalContainer.RegisterType<TStorageService>.Implements<IStorageService>.DelegateTo(
function: TStorageService
begin
Result := TStorageService.Create();
Result.Path:= 'MyButton';
end).AsSingleton;
GlobalContainer.Build;
end;
end.
In the constructor TMyButton.Create (AOwner: TComponent) I want to replace the ServiceLocator with field injection, but I cannot find how to do it.
An example, but it doesn't work. I don't see the problem.
unit uObjects;
interface
uses
Vcl.StdCtrls,
System.Classes,
uStorageInterface,
Spring.Container,
Spring.Services,
Spring.Container.Common;
type
TMyButton= class (TButton, IStorageObject)
private
[Inject]
FStorage: IStorageService;
function GetStorage: IStorageService;
protected
procedure DoExit; override;
public
constructor Create(AOwner: TComponent); override;
end;
implementation
{ TMyButton }
constructor TMyButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
//FStorage:= ServiceLocator.GetService<IStorageService>;
end;
procedure TMyButton.DoExit;
begin
inherited;
if assigned(FStorage) then
begin
self.Caption:= FStorage.Path;
end;
end;
function TMyButton.GetStorage: IStorageService;
begin
Result:= FStorage;
end;
end.
procedure TDataModule2.DataModuleCreate(Sender: TObject);
begin
GlobalContainer.RegisterType<TStorageService>.Implements<IStorageService>.DelegateTo(
function: TStorageService
begin
Result := TStorageService.Create();
Result.Path:= 'MyButton';
end).AsSingleton;
GlobalContainer.RegisterType<TMyButton>.Implements<IStorageObject>.InjectField('FStorage');
GlobalContainer.Build;
end;
When I create an A TMyButton at runtime, the FStorage in the TMyButton is nill. When I use FStorage: = ServiceLocator.GetService; in the constructor then FStorage is assigned. But I want to use injection and not ServiceLocator. If possible.
source to share
At first the Register is TMyButton
incorrect. The container won't fill the thread for AOwner by itself. This means it will fall back to the TObject constructor, leaving the button instance half-initialized.
To use TComponent ctors, use an explicit subdependency resolver or explicitly register it that way.
GlobalContainer.RegisterType<TMyButton>.Implements<IStorageObject>
.DelegateTo(
function: TMyButton
begin
Result := TMyButton.Create(nil);
end)
.InjectField('FStorage');
Now attention! If you resolve this as a TMyButton somewhere, the container doesn't know how to solve it because you specified IStorageObject as the service type. If you specify a service type, the container will not resolve the class unless it is explicitly defined. However, there is a slight glitch here as the container tries to register this class again when it resolves. This results in a second registration for the TMyButton, which does not have the specified field injection. I'll fix it.
Until then, you can fix this by adding Implements<TMyButton>
to your registration.
source to share