Search code examples
c#initializationdryioc

DryIoc Call async initialization method before instance resolve


I have a class A. It must be a singleton and the async Initialization method must be called when it is created. I did not find suitable functionality in the DryIoc documentation. It seems to me that the RegisterDelegate method is the closest, but it does not take an asynchronous lambda as a parameter.

For clarity, I will give an example to be in front of your eyes:

public class A
{
    ...
    public async Task<A> Initialization() {...}
    ...
}

var instance = await new A().Initialization();

Solution

  • Answer 1

    The async initialization is not yet supported by the DryIoc because of the two-colored function problem and the associated complexity.

    Basically, I need to introduce the separate await call graph generated for the whole chain of dependencies where some of them are async starting from the ResolveAsync or similar API.

    In the absence of it, you may use the approach discussed at the end of the article https://endjin.com/blog/2023/01/dotnet-csharp-lazy-async-initialization.

    In this approach, you are moving the await part of the initialization outside the construction phase into the method(s).

    So you may add the async Initialization as a Func dependency into the constructor:

    public class A
    {
        readonly Task _finishInitAsync;
        
        // Actually kicking off the initialization part in the constructor to start the job,
        // but postpone the await of the already finished or in-progress initialization until the first using method.
        // Alternatively, you may use Lazy<Func<A, Task>> parameter instead to postpone the whole initialization until the using method call.
        public A(Func<A, Task> initAsync) => _finishInitAsync = initAsync(this); 
        
        // Making it static to detach from the A and simplify the passing it around
        public static async Task Initialization(A a) {...}
        ...
        public async Task MethodUsingTheInit() 
        {
           await _finishInitAsync();
           // do stuff
        }
    
    }
    
    container.Register<A>();
    container.RegisterDelegate<Func<A, Task>>(_ => a => A.Initialization(a));
    
    var a = container.Resolve<A>();
    await a.MethodUsingTheInit();
    

    Answer 2

    You may go full manual mode, which appears much simpler to me. Call the Initialization manually before the first use.

    // `New` allows to construct the A by injecting its dependencies from the container,
    // but without actually registering the A itself.
    var a = container.New<A>();
    
    await A.Initialization(a);
    
    // Then register the fully initialized A into container
    container.RegisteInstance(a);
    

    Feedback

    I am very interested on any ideas for this topic as well as for the alternative solutions.