0

This is my second attempt to explain what I am trying to accomplish as I did not explain it very well before.

for this Pseudo example if you have the classes to resolve setup as below;

TOuter.constructor.Create(Name: TName; Inner1, Inner2: TInner);
TInner.constructor.Create(Name: TName; Sub: TSub);
TSub.constructor.Create(Name: TName);
TName.constructor.Create(Name: String);
var Names = TStack<String>.Create;

If it is possible to intercept before and after resolve it would be possible to implement a parent, ancestor or, 'i am being constructed for ...'.

for example

RegisterType<TName>(
  function: TName
    begin
      Result:=TName.Create(String.Join('.',Names.List.ToArray));
    end
);

RegisterType<TOuter>;
RegisterType<TInner>;
RegisterType<TSub>;

Resolver.BeforeResolve:=
    procedure(ClassType: TClass)
    begin
      Names.Push(ClassType.ClassName);
    end;

Resolver.AfterResolve:=
    procedure(ClassType: TClass)
    begin
      Names.Pop;
    end;

Resolve<TOuter> would produce

TOuter
    Name = 'Outer'
    Inner1 = 
        Name = 'Outer.Inner'
        Sub
            Name = 'Outer.Inner.Sub'
    Inner2
      Name = 'Outer.Inner'
        Sub 
          Name = 'Outer.Inner.Sub'
            

I did think it might be possible by sub classing the component activator and looking for if classname=TName or if arguments.contains(TName) then ...

For this example I have omitted making the Names stack handle multiple threads and in reality I am not using names but rather having a stack of interfaces each knowing who their 'parent' is but this demonstrates what I am trying to implement.

Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102
Richard Shuck
  • 113
  • 1
  • 10

1 Answers1

0

Instance creation is ultimately done within the component activator. If you create a decorator for that you can solve your requirement.

I will just write down the code - you can package this into a container extension that does all the stuff automatically (look into the Spring.Container.*Extension units).

For brevity, I will leave out all standard creation and cleanup code and any code to make this thread-safe.

type
  TComponentActivatorDecorator = class(TInterfacedObject, IComponentActivator)
  private
    fActivator: IComponentActivator;
  public
    constructor Create(const activator: IComponentActivator);
    function CreateInstance(const context: ICreationContext): TValue;
  end;

type
  TComponentActivatorBaseAccess = class(
    Spring.Container.ComponentActivator.TComponentActivatorBase)
  end;

function TComponentActivatorDecorator.CreateInstance(
  const context: ICreationContext): TValue;
var
  name: string;
begin
  // yes, this is a bit ugly because the information about the handled type 
  // is not passed to the method but a protected state of the activator
  // actually this should (and actually is but only internally) be part of
  // the ICreationContext - this might change in the future
  name := TComponentActivatorBaseAccess(TObject(fActivator)).Model.ComponentType.Name;
  Names.Push(name);
  try
    Result := fActivator.CreateInstance(context);
  finally
    Names.Pop
  end;
end;

After the call to Build you run this code:

  for Model in GlobalContainer.Registry.FindAll() do
  begin
    if Model.ComponentActivator is TReflectionComponentActivator then
      Model.ComponentActivator := TComponentActivatorDecorator.Create(Model.ComponentActivator);
  end;

Now every activator that does the object creation via reflection (so not the ones doing by a delegate) is being decorated and executes the extra push/pop logic and you access that in your TName delegate.

I declared TName as follows:

type
  TName = type string;

and its registration:

GlobalContainer.RegisterType<TName>(
  function: TName
  begin
    Result := string.Join('.',Names.ToArray);
  end);

Here is how a resolved TOuter instance looks now:

enter image description here

Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102