如何正确注入一个属性来形成?

时间:2014-10-23 15:43:25

标签: delphi dependency-injection inversion-of-control spring4d

我将在第二时间提出这个问题。 请不要怪我。

情况:

我有一张表格

TfrmMain = class(TForm)
private
   [Inject('IniFileSettings')]
   FSettings: ISettings;
public
end;

我有容器初始化程序:

procedure BuildContainer(const container: TContainer);
begin
  container.RegisterType<TIniSettings>.Implements<ISettings>('IniFileSettings');

  container.RegisterType<TfrmMain, TfrmMain>.DelegateTo(
    function: TfrmMain
    begin
      Application.CreateForm(TfrmMain, Result);
    end);

  container.Build;
end;

所以我通过容器初始化TfrmMain和TIniSettings。

<。>在.DPR我有:

begin
  BuildContainer(GlobalContainer);
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TfrmMain, frmMain);
  Application.Run;
end.

我也有TApplication的帮手:

procedure TApplicationHelper.CreateForm(InstanceClass: TComponentClass; var Reference);
var
  locator: IServiceLocator;
begin
  locator := TServiceLocatorAdapter.Create(GlobalContainer);
  if locator.HasService(InstanceClass.ClassInfo) then
    TObject(Reference) := GlobalContainer.Resolve(InstanceClass.ClassInfo).AsObject
  else
    inherited CreateForm(InstanceClass, Reference);
end;

问题: 当我尝试

procedure TfrmMain.FormCreate(Sender: TObject);
begin
   s := FSettings.ReadString('Connection', 'Server', 'localhost');
end;

我得到AV异常,因为FSettings目前是NIL。

从容器中获取FSettings对象的正确方法是什么?

更新

FSettings := GlobalContainer.Resolve<ISettings>;

此行完美运行...与上次使用[Inject]属性时有问题。 即使使用Stefan的解决方案,我也可以使该方法有效:

How to initialize main application form in Spring4D GlobalContainer?

1 个答案:

答案 0 :(得分:6)

首先,容器不再具有HasService的原因是因为该方法已被删除。您可以按如下方式访问它:

if container.Kernel.Registry.HasService(...) then  // yeah yeah, I know LoD is crying right now ;)

我会避免使用ServiceLocator和GlobalContainer进行混音。虽然它们应该指向相同的实例,但可能并非如此,因为实际上有人可能会将其中一个指向另一个实例。如果您确实想在这种情况下使用ServiceLocator,那么也可以从ServiceLocator解析。但请记住,容器上没有任何信息(即使你必须调用内核的某些不同部分。

但这不是您在设置注入时面临的问题。你遇到的问题是时机问题。 FormCreate方法(我猜它是附加到OnCreate事件)。因此容器实例化TfrmMain,事件被调用然后返回到容器代码,然后进行所有注入。因此,在构造期间调用某些代码中没有通过构造函数注入的东西是一种时间耦合。

这个问题有不同的方法:

  • 将您对FSettings的访问权限转移到稍后触发的某个事件(如OnShow或OnActivate)
  • 不要使用现场注入它可能很好但是将你的代码耦合到容器,因为&#34;传统&#34;代码不能那样做。使用属性注入和执行代码的setter。

当你考虑构造函数注入时,对于必需的依赖项和属性注入那些是可选的,我会说去构造函数注入。但是知道你使用TComponent后代我可能会在这种情况下使用属性注入,尽管该依赖项不是可选的。