8 Stimmen

Delphi - wie man einen Parameter vom Instantiierer an einen Konstruktor im Spring4D Dependency Injection Framework übergibt?

Ist es möglich, eine Klasse mit einem erwarteten Parameter zu registrieren, der von dem Erstellungspunkt aus übergeben werden soll?

Ich weiß, dass so etwas getan werden kann:

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

Aber dort sind die Parameter an den Ausführungskontext gebunden, an dem der Container registriert wird und nicht, wo das Objekt instanziiert wird.

Ist so etwas zum Beispiel möglich?

GlobalContainer.Resolve([FCurrentUser]);

Ich weiß, dass einige Leute dafür plädieren, sehr einfache Konstruktoren zu haben, aber manchmal scheint ein Konstruktorparameter der richtige Weg zu sein:

  1. Das erstellte Objekt benötigt das Objektparameter, um zu funktionieren, daher muss die Referenz erfüllt sein. Der Parameter macht diese Beschränkung auch beim Blick auf die Klasse viel offensichtlicher.

  2. Sie können die Referenz in einer Methode oder Eigenschaft zuweisen und in jeder anderen Methode eine Ausnahme auslösen, wenn Sie versuchen, das Objekt zu verwenden, ohne die Zuweisung zuerst vorzunehmen. Ich mag es nicht, diesen Code zu schreiben, es ist einfach eine Zeitverschwendung, verwenden Sie einfach den Konstruktorparameter und überprüfen Sie dort. Weniger Code ist meiner Meinung nach besser.

  3. Auch das übergebene Objekt ist lokal für das Objekt, das das neue Objekt unter Verwendung des Containers erstellt (zum Beispiel ein Transaktionsobjekt) und hat einen bestimmten Zustand (es handelt sich nicht um ein neues Objekt, das ich mit dem Container abrufen kann).

7voto

Stefan Glienke Punkte 19667

Ich habe Resolverüberschreibungen wie in Unity hinzugefügt.

Sie können also schreiben:

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.Implements;
  GlobalContainer.Build;

  GlobalContainer.Resolve(
    TOrderedParametersOverride.Create([TUser.Create]));

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

  Readln;
end.

Bearbeitung vom 17.09.2018:

Dies hat sich vor einiger Zeit geändert und Resolve akzeptiert Parameter über array of TValue. Sie können entweder Werte direkt dort übergeben, die dann genau mit der Parameterliste im Konstruktor übereinstimmen müssen. Wenn Sie die Parameter nur teilweise füllen möchten, können Sie TNamedValue oder TTypedValue aus Spring.pas verwenden.

Auf jeden Fall schlage ich vor, Fabriken zu verwenden, die von Ihrem Code injiziert und verbraucht werden, und nicht in die Resolve-Methode des Containers (global oder nicht) zu greifen.

0voto

Atle S Punkte 229

Sie könnten diesen Thread überprüfen: https://forums.embarcadero.com/message.jspa?messageID=440741

Ich habe eine Implementierung in diesem Framework gemacht und benutze es in meinem Code. Wenn Sie es wollen, schicke ich es Ihnen. Beachten Sie dabei, dass dies nicht unbedingt der richtige Weg ist, wenn und falls sie eine Funktionalität wie diese im Code hinzufügen. Baoquan hat meinen Code.

Diese Implementierung behandelt auch den Fall, wenn Sie versuchen, ohne alle erforderlichen Parameter aufzulösen, und gibt Ihnen eine Ausnahme, die die fehlenden Parameter anzeigt.

Sie verwenden es so:

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

Der Wert wird als TValue gespeichert und kann beliebig sein.

Das Registrieren des Parameters mit Ihrer Klasse erfolgt mit Attributen wie diesem:

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

Sie können auch folgendes tun

[ContainerParameter]
FYourValue1:.. 

Aber dann hat Ihr Parameter 'FYourValue1' als Namen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X