delphi - как передать параметр из инстанциатора в конструктор в инфраструктуре внедрения зависимостей spring4d? - PullRequest
8 голосов
/ 31 марта 2012

Можно ли зарегистрировать класс с параметром, который, как ожидается, будет передан с точки создания?

Я знаю, что это можно сделать примерно так:

GlobalContainer.RegisterType<TUserProcessor>.Implements<IUserUpgrader>.
AsTransient.DelegateTo(
    function: TUserProcessor
    begin
      Result := TUserProcessor.Create(GetCurrentUser);
    end
  );

Но там параметры привязаны к контексту выполнения, в котором регистрируется контейнер, а не там, где объект получает объект.

Что-то подобное, например, возможно?

GlobalContainer.Resolve<IMathService>([FCurrentUser]);

Я знаю, что у некоторых защитников есть очень простые конструкторы, но бывают случаи, когда параметр конструктора явно выглядит так:

  1. Созданному объекту необходим параметр объекта для работы, поэтому ссылка должна быть удовлетворена. Параметр также делает это ограничение намного более очевидным, глядя на класс.

  2. Вы можете назначить ссылку в методе или свойстве, а также вызвать и исключить в любом другом методе, если попытаетесь использовать объект без предварительного назначения. Мне не нравится писать этот тип кода, это просто пустая трата времени, просто используйте параметр конструктора и проверьте там. Чем меньше кода, тем лучше ИМО.

  3. Кроме того, передаваемый объект является локальным по отношению к объекту, который создает новый объект с использованием контейнера (например, объект Transaction) и имеет некоторое состояние (это не новый объект, который я могу получить с помощью контейнера).

Ответы [ 2 ]

7 голосов
/ 23 мая 2012

Я добавил переопределения преобразователя, как в Unity.

Так что вы можете написать:

program Demo;

{$APPTYPE CONSOLE}

uses
  Classes,
  Spring.Container,
  Spring.Container.Resolvers;

type
  IUserUpgrader = interface
    ['{ADC36759-6E40-417D-B6F7-5DCADF8B9C07}']
  end;

  TUser = class(TObject);

  TUserProcessor = class(TInterfacedObject, IUserUpgrader)
  public
    constructor Create(AUser: TUser);
  end;

constructor TUserProcessor.Create(AUser: TUser);
begin
  Writeln('called constructor with passed user');
end;

begin
  GlobalContainer.RegisterType<TUserProcessor>.Implements<IUserUpgrader>;
  GlobalContainer.Build;

  GlobalContainer.Resolve<IUserUpgrader>(
    TOrderedParametersOverride.Create([TUser.Create]));

  GlobalContainer.Resolve<IUserUpgrader>(
    TParameterOverride.Create('AUser', TUser.Create));

  Readln;
end.

Редактировать 17.09.2018:

Этоизменилось довольно давно, и Resolve принимает параметры через array of TValue.Вы можете либо передать значения непосредственно туда, которые затем должны точно соответствовать списку параметров в ctor.Для только частичного заполнения параметров вы можете использовать TNamedValue или TTypedValue из Spring.pas

В любом случае я предлагаю использовать фабрики, которые будут внедряться и потребляться вашим кодом, а не попадать в контейнер (глобальный или нет).) Разрешить метод.

0 голосов
/ 06 мая 2012

Вы можете проверить эту ветку: https://forums.embarcadero.com/message.jspa?messageID=440741

Я реализовал реализацию этого в фреймворке и использовал его в своем коде.Если хочешь, я тебе пришлю.Имейте в виду, что это не так, как это может быть сделано, когда и если они добавляют такую ​​функциональность в кодовую базу.У Baoquan есть мой код.

Эта реализация также обрабатывает, если вы пытаетесь разрешить без всех необходимых параметров, и выдает исключение, показывающее пропущенные параметры.

Вы используете его так:

ServiceLocator.GetService<ISomeObj>(ServiceParameters['paramname1',value1]['paramname2',value2]..);

Значение хранится в виде TValue и может быть любым.

Регистрация параметра в вашем классе выполняется с помощью таких атрибутов:

private
  [ContainerParameter('paramname1')]
  FYourValue1: ...;
  [ContainerParameter('paramname2')]
  FYourValue2: ..;

Вы также можете сделать

[ContainerParameter]
FYourValue1:.. 

Но тогда ваш параметр будет иметь имя FYourValue1.

...