The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,262 other subscribers

A bunch of Spring4D dependency injection container related questions

Posted by jpluimers on 2020/11/19

Since G+ is down, a lot of interesting questions have vanished.

Luckily I saved some by [WayBack] Jacek Laskowski – Google+ related to Spring4D and dependency injection:

[WayBack] Spring4D IoC and specific singleton… – Jacek Laskowski – Google+

First my Google Groups archival reminder:

  1. Rename from
  2. To
  3. Save that URL with Archive.is

Q: [WayBack] Spring4D – How to get the same instance of Logger for a class group?… – Jacek Laskowski – Google+

Everything works on interfaces, but I skip interfaces for simplicity.

I have a base class:

 TBaseClass = class
  fLogger : TLogger;
 end;

Two usable classes are inherited from it:

 TClassA = class(TBaseClass)
  fWorkClass1 : TWorkClassA;  
  fWorkClass2 : TWorkClassB;
 end; TClassB = class(TBaseClass)
  fWorkClass3 : TWorkClassC;
  fWorkClass4 : TWorkClassD;
 end;

…which have working classes:

 TWorkClassA = class
  fLogger : TLogger;
 end; TWorkClassB = class
  fLogger : Tlogger;
 end; TWorkClassC = class
  fLogger : TLogger;
 end; TWorkClassD = class
  fLogger : Tlogger;
 end;

These objects I use directly in the code:

var
 ObjectA : TClassA;
 ObjectB : TClassB;

ObjectA and ObjectB create their own separate workgroups, and I would like all classes in the group to use the same fLogger instance.

I would like to create two instances of TLogger:

  • first for objects: ObjectA, fWorkClass1, fWorkClass2
  • second for object: ObjectB, fWorkClass3, fWorkClass4

How to register and get from Spring4D?


A: [Archive.is] [IoC and DI] Specific instances for object groups – Google Groups

Honza Rameš

Hi Jacek,

I’d recommend using named injections. (Stefan has some surprises that might allow you to achieve similar functionality differently, stay tuned for 1.3 ;-) ).

Honza

Stefan Glienke
You can write your own resolver that handles TLogger and injects the same instance based on some criteria (like a Logger(‘groupname’) attribute on your injection target or something like that)

Q: [WayBack] How to debug resolver issues in Spring4D?… – Jacek Laskowski – Google+

How to debug resolver issues in Spring4D?
When in constructor occur unregistered type parameter (or registered, but uncompletly – without register his constructor parameters types), Spring creates the object with a bad constructor (without parameters, come from base class), but does not warn about it!
And an unfinished object is created, with nil in the key fields.

How to avoid this, how to detect it?

A:

  • Baoquan Zuo's profile photo
    It also happened to me yesterday, in my case, it was caused by a RTTI directive which prevent Spring container locating constructor in component type as compiler didn’t emit them. I wanted to post in the group about adding trace log and warnings about possible errors.
  • Jacek Laskowski's profile photo
    In my case class has interface, but I register only class without explicitly register interface (RegisterType<TMyClass>) and container found and register only interface, and when I try resolve class then class not found. After correct code to RegisterType<TMyClass, TMyClass> problem disappear. But trace log would by very useful!
  • Stefan Glienke's profile photo
    There is the unit [WayBack]Spring.Container.ActivatorExtension which has a container extension that prevents the container to fall back on the parameterless ctor.
  • Jacek Laskowski's profile photo
  • Stefan Glienke's profile photo
    Add the extension to the container.

Q: [WayBack] Spring resolver problem… I try resolve factory method (previously registered in container) and I get compile error… – Jacek Laskowski – Google+

Spring resolver problem…
I try resolve factory method (previously registered in container) and I get compile error:

{$M+}
TCompaniesManagerFactory = reference to function(const aAuthKey : RAuthKey) : ICompaniesManager;
{$M-}

var
lCompaniesManagerFactory : TCompaniesManagerFactory;
begin
lCompaniesManagerFactory := Container.Resolve<TCompaniesManagerFactory>; // <- line with error

Error:

[dcc32 Error] E2010 Incompatible types: 'TCompaniesManagerFactory' and 'Procedure of object'

ps. I know about composition root and others IoC rules… please tell me why I get compile error, types are compatible

A:

Put parentheses on your call. When assigning a function call result to an anonymous method variable it does not consider calling the method to get an assignment compatible result. Most simple case:

uses
  System.SysUtils;

function GiveProc: TProc;
begin
end;

var
  p: TProc;
begin
  p := GiveProc; // <- E2010 - write GiveProc() and it works
end.


Q: [WayBack] Spring4D, register factory… default parameters… – Jacek Laskowski – Google+

Sprin4D, register factory

I have a class that implements the interface:

TMyClass = class(TInterfacedObject, IMyInterface)
public
constructor Create(ParamA : string; ParamB : Integer; ParamC : Boolean);
end;

and the factory:

TMyInterfaceFactoryA = reference to function(ParamA : string; ParamB : Integer; ParamC : Boolean) : IMyInterface;

which I register in the spring:

Container.RegisterType<IMyInterface, TMyClass>;
Container.RegisterFactory<TMyInterfaceFactoryA>;

Then I resolve the factory and create an interface, it works ok.

But I would like a second factory that will always give ParamC a constant value, e.g. True

I tried this:

TMyInterfaceFactoryB = reference to function(ParamA : string; ParamB : Integer) : IMyInterface;

but I get error when resolving:

'Unsatisfied constructor on type TMyClass'

How to declare and register such a factory?

A

  • +Stefan Glienke
    Of course you were right, everything works correctly. The problem for me was the incomplete registration and “losing” by the container of one of the constructors.
    Is it possible to force Spring to report an unknown, unregistered type used in the constructors?
    At the moment, the report is poor, with a complex hierarchy of classes and extensive constructors a lot of time is lost in searching for missing registrations :-(Eg.classes:TUnknownClass = class
    end;TExampleClass = class
    public
    constructor Create(aUnknown : TUnknownClass);
    end;{$M+}
    TExampleClassFactory = reference to function : TExampleClass;
    {$M-}registrations:aContainer.RegisterType<TExampleClass>;
    // >>>>>> missing TUnknownClass type registration
    aContainer.RegisterFactory<TExampleClassFactory>;usage:
    var
    lExample: TExampleClass;
    begin
    lExample := aExampleClassFactory();

    and error:

    'Unsatisfied constructor on type: ss.Framework.Base.SafeQueryProvider.TExampleClass'

    There is only information WHERE is missing and not WHAT.

    Would it be possible to detect the lack of TUnknownClass registration during the TExampleClass class registration and add this fact in the message? I lost a few hours to look for such details, and if Spring were to report it would be wonderful :-)

  • Stefan Glienke's profile photo

    “Would it be possible to detect the lack of TUnknownClass registration during the TExampleClass class registration and add this fact in the message?”

    No, because order of registration does not have to be like that, can easily register TUnknownClass after that or provide it by some other way. Proper ctor to be called are currently determined at resolve time.

    Even when calling container.Build it currently is not possible to determine that because type registrations are only processed after each other. For that to work at that point it would need to do a second run checking and selecting ctors after all types are known to the container. It even is something that I want for quite some time because that would improve resolve performance significantly because it can already build a plan of what to create without determining that over and over.

    What we are doing at work is putting some information out via the ILogger interface inside the type resolver so we can see in the log what ctors it inspects and which it picks (or does not).

    That being said I have been planning quite some changes for 1.3 that also touch on this topic because once your object graph gets huge using the container requires some way of diagnostic possibilities. But the curse of the current flexibility of the container makes this not so easy as there are so many corner cases and exceptional use cases that are all valid that I don’t want to break (or at least be aware of to put them into some breaking change list and provide alternative ways to solve the task)

  • Jacek Laskowski's profile photo
    +Stefan Glienke Ok, it does not have to be checked at the registration stage or even Build. It would be enough for the resolver, in the place where it analyzes the available constructors, to detect the type that is not registered, and then that this information would be passed higher and if an exception occurs, its message would contain information about this unknown type.
    In general, I would like to get info about the fact that the resolver does not know the TUnknownClass type when trying to create a TExampleClass object. That’s all. I know it’s not trivial :-)
    Thanks for you works, I waiting for S4D 1.3

Q: [WayBack] Spring4D IoC and specific singleton… – Jacek Laskowski – Google+

Spring4D IoC and specific singleton

I have a generic interface to write data to the database: IDBStorer. This interface can write data to any pre-defined table (generates inserts and updates). I would like the group of objects that saves, for example, orders, to use one instance of this interface, but configured to store orders. Completely in other place, another group of objects saves, for example, car routes, also uses the IDBStorer interface, but with a completely different configuration.

Therefore, I would need to resolve the interface from the factory using some type of “order” or “route” and to get the same instance for each “route” request, but different than when I resolve for “order”.

Something like this:

OrdersStorer := Container.Resolve<IDBStorer>.NamedSingleton('orders');
RouteStorer := Container.Resolve<IDBStorer>.NamedSingleton('route');

or better by factory:

TStorerFactory = reference to function(aStorerIdentifier : string) : IDBStorer;
Container.RegisterFactory<TStorerFactory>.AsNamedSingleton;

OrderStorer := StorerFactory('orders');
OrderStorer2 := StorerFactory('orders');
RouteStorer := StorerFactory('route');

and I get one instance IDBStorer per unique string, so OrderStorer is equal in this case OrderStorer2 (because resolved by this same unique identifier ‘orders’)

Is it possible?

A

  • Stefan Glienke's profile photo
    I already answered your question on the forum. If you dont like the multiple registrations per “key” for the singletons then use the most obvious: a dictionary where you put in your instances per key and fetch them from there which you can abstract just fine with a factory(key
  • Jacek Laskowski's profile photo
    It looks like I have to write my own LifeTimeManager and register the type with this manager, is this a good clue?
  • Jacek Laskowski's profile photo
    +Stefan Glienke Where should I put this dictionary? I would like to use the benefits of Spring and entrust him with managing such things. Perhaps in LifetimeManager, similar to TSingletonPerThreadLifetimeManager?
  • Stefan Glienke's profile photo

    Do whatever you want but a DI container is not a multipurpose swiss army knife design pattern. It is for creating object graphs. And your object graph by definition has 2 (or more) different instances of IDBStorer instances for a given key (string, or type of object it handles or whatever). The definition of your object graph is what happens in the registrations.

    As you can see in my example that I gave you I am exactly doing that “using the benefits”. I am putting 2 different registrations (in the example they look the same except their name). Now you could even imagine that the one gets also other dependencies passed than the second (maybe the one needs a TOrderValidator passed as IValidator and the other needs a TRoutesValidator – can easily put that in the registrations and model your object graph). Or one day you find out that you cannot use the same implementation at all for those two and you can easily change that by switching the registrations for one or both to not use the same implementing class but different ones.

    Everything else is hack that will fall onto your feet sooner or later.

  • Jacek Laskowski's profile photo
    +Stefan Glienke Yes! I calmly rethinked about what you wrote, you are absolutely right! Yesterday I thought I was not understood, but it is I who complicate the simple things very much. I forgot about KISS.
    Thanks for comment.
  • Jacek Laskowski's profile photo

    +Stefan Glienke One more question:

    I do not want to use the IDBStorer interface directly in the constructors of other classes, but I want to use the factory method.
    How to declare and register a factory to get the same effect?

    TDBFactory = reference to function (aSingletonIdentifier: string): IDBstorer;

  • Stefan Glienke's profile photo
    RegisterInstance<TDBFactory>(function(aSingletonIdentifier: string): IDBStorer begin Result := container.Resolve<IDBStorer>(aSingletonIdentifier); end);
  • A. Bouchez's profile photo
    If you use singletons, why not just use a global variable? I don’t like singletons for sure. ;)
  • Stefan Glienke's profile photo
    +A. Bouchez Singleton in DI means not the same as in the design pattern. It means that it is ensured that there is only one instance every time I ask for this as a dependency but not a new instance each time. And the subtle but important difference is that the singletoness is not baked into the class itself but controlled by the container and how you configure things.
  • A. Bouchez's profile photo
    +Stefan Glienke You are right. I remember now – I already made this confusion some time ago… same naming is IMHO wrong. “Shared” or “Common” could be less confusing for sure. In our framework we use “sicShared” for such an instance livetime.
  • Stefan Glienke's profile photo
    +A. Bouchez I did not invent the naming but it is called like that in most DI frameworks. Being really pedantic though I would say that it is an implementation detail of the pattern that is the bad thing and when you do DI the implementation of it is good as it is not coupled to the core responsibility of the class and not part of the class itself but of the mechanism that creates them.

The Spring4D forum thread: [Archive.is] Concrete instance of singleton – like a multiple singleton? – Google Groups

Jacek Laskowski
I have an ISomething interface. I would like a certain group of objects to use a specific instance of ISomething (A), and another group of another ISomething (B) instance.
So there is one interface, one implementation, but two instances of this interface are created, which many other objects use.
How to register this interface (maybe with identifier?) in the container and how to resolve instance A or B via the factory method?
Philipp Schäfer

As far as I am aware, you can only name the registration and overwrite the resolving mechanich of each and every depend type (by specifying the named value and nil for the other constructor parameters). This specification is made during the registration phase. I do not recall the exact method name.

This will be improved in upcoming versions.

Another possibly more simple approach might be to derive two interfaces from ISomething with no further method. And register implementation A and B as singletons to these derived interfaces.
If you want to dive really deep you could influence the resolving process by a container extension. We do this for something else in one of our projects. This takes probably the most effort and might be subject to changes in updates.
Sorry that I talk mostly out of my head here, as I am on mobile only.
{$APPTYPE CONSOLE}
 
uses
  Spring.Container,
  System.SysUtils;
 
type
  ISomething = interface
    ['{829E67EE-EA29-4D90-8CD3-558DB168F150}']
  end;
 
  TSomething = class(TInterfacedObject, ISomething)
  end;
 
  TCompBase = class
  private
    fSomething: ISomething;
  public
    constructor Create(const something: ISomething);
    property Something: ISomething read fSomething;
  end;
 
  TCompA = class(TCompBase);
  TCompB = class(TCompBase);
  TCompC = class(TCompBase);
 
constructor TCompBase.Create(const something: ISomething);
begin
  fSomething := something;
end;
 
var
  a,b,c: TCompBase;
begin
  GlobalContainer.RegisterType<ISomething, TSomething>('somethingA').AsSingleton;
  GlobalContainer.RegisterType<ISomething, TSomething>('somethingB').AsSingleton;
  GlobalContainer.RegisterType<TCompA>.InjectConstructor(['somethingA']);
  GlobalContainer.RegisterType<TCompB>.InjectConstructor(['somethingB']);
  GlobalContainer.RegisterType<TCompC>.InjectConstructor(['somethingB']);
  GlobalContainer.Build;
  a := GlobalContainer.Resolve<TCompA>;
  b := GlobalContainer.Resolve<TCompB>;
  c := GlobalContainer.Resolve<TCompC>;
  Writeln(a.Something = b.Something);
  Writeln(a.Something = c.Something);
  Writeln(b.Something = c.Something);
end.
Jacek Laskowski
I can’t do this on two interfaces or two registrations.
In fact, these groups of objects using one singleton can be much more. I will describe it specifically.I have a generic interface to write data to the database: IDBStorer. This interface can write data to any pre-defined table (generates inserts and updates). I would like a group of objects that saves, for example, orders, to use one instance of this interface, but configured to store orders. Completely in other place, another group of objects saves, for example, car routes, also uses the IDBStorer interface, but with a completely different configuration.Therefore, I would need to resolve the interface from the factory using some type of “order” or “route” and to get the same instance for each “route”, but different than when I resolve for “order”.Something like:OrdersStorer := Container.Resolve<IDBStorer>.NamedSingleton('orders');
RouteStorer := Container.Resolve<IDBStorer>.NamedSingleton('route');or better by factory:TStorerFactory = reference to function(aStorerIdentifier : string) : IDBStorer;
Container.RegisterFactory<TStorerFactory>.AsNamedSingleton;OrderStorer := StorerFactory('orders');
RouteStorer := StorerFactory('route');and I get one instance IDBStorer per unique string.Is possible?

 

 

–jeroen

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.