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.

+3


source to share


1 answer


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.

+1


source







All Articles