Как заставить контейнер игнорировать атрибут `Inject` для определенного типа? - PullRequest
0 голосов
/ 09 ноября 2018

Сначала был один контейнер, настроенный для размещения одного экземпляра (AsSingleton) в качестве корня композиции. Затем эта настройка была перемещена в другой контейнер, чтобы разрешить из него несколько таких одноэлементных контейнеров, которые будут использоваться новым корнем композиции. Кроме того, корень композиции одноэлементных контейнеров использует атрибут Inject метода. Теперь и внутренний одноэлементный контейнер, и внешний контейнер обрабатывают атрибут Inject. Однако это требуется только для внутреннего контейнера-одиночки, который отвечает за создание экземпляра, а не для внешнего контейнера, который связывает только время жизни контейнера-одиночки с разрешенным экземпляром.

Как заставить (внешний) контейнер игнорировать атрибут Inject для определенного типа?


(упрощенный) пример :

unit Example;

interface

uses
  Spring.Container,
  Spring.Container.Common;

type
  TComponentB = class
  end;

  TComponentA = class // a TForm descendant
    [Inject]
    procedure Init(const ComponentB: TComponentB);
  end;

procedure Register(const Container: TContainer);
procedure Run;

implementation

{ TComponentA }

procedure TComponentA.Init(const ComponentB: TComponentB);
begin
  WriteLn('TComponentA.Init');
end;

{ Routines }

procedure Register(const Container: TContainer);
const
  ComponentAContainer = 'ComponentAContainer';
begin
  Container.RegisterType<TContainer>(ComponentAContainer).DelegateTo(
    function : TContainer
    begin
      Result := TContainer.Create;
      Result.RegisterType<TComponentA, TComponentA>.AsSingleton;
      // The inner container invokes `[Inject] TComponentA.Init`.
      // This succeeds as the inner container has `TComponentB`
      // registered.
      // This is the desired mechanism of choice to construct
      // the object graph.
      Result.RegisterType<TComponentB, TComponentB>;
      Result.Build;
    end
  );
  Container.RegisterType<TComponentA>.DelegateTo(
    function : TComponentA
    var
      Temp: TContainer;
    begin
      Temp := Container.Resolve<TContainer>(ComponentAContainer);
      Result := Temp.Resolve<TComponentA>;
    end
  );
  // The outer container invokes `[Inject] TComponentA.Init`, too.
  // This does not succeed as the outer container does not have
  // `TComponentB` registered.
  // In fact, this is not desired anyway, as the inner container
  // already constructed the complete object graph for a `TComponentA`
  // instance.
  // Here only the binding of the lifetimes of Temp and Result are
  // supposed to happen (not shown).
end;

procedure Run;
var
  Container: TContainer;
  ComponentA: TComponentA;
begin
  Container := TContainer.Create;
  Register(Container);
  Container.Build;
  ComponentA := Container.Resolve<TComponentA>;
  ComponentA := Container.Resolve<TComponentA>;
  { ... }
end;

end.

Примечания. Для простоты в этом примере не учитывается управление временем жизни. Кроме того, внутренний контейнер содержит на самом деле более одного синглтона, и было бы много работы, чтобы вручную связать их с графом объектов и не использовать AsSingleton из контейнера, что делает его опцией без параметров.

...