Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async Initialisation for Transient ASP.NET core

I have a transient service that has to do async initialization (connecting to a different server). It exposes a property public Task InitTask which one can wait till the service is initlized. It also exposes subservices that can be savely accessed after the InitTask has finished.

public class Service
{
    public Task InitTask { get; }

    public ISubService1 SubService1 { get; }
    public ISubService2 SubService2 { get; }
    public ISubService3 SubService3 { get; }
}

All functionallity that the connection to the other server provides is capulated by these sub services. Normally I would inject the main service and then wait for it to be finished initlaization and then use one of these sub services. But I would like to just inject these subservices.

At first I tried

services.AddTransient<Service>()
    .AddTransient(provider =>
    {
        var server = provider.GetService<Service>();
        return server.SubService1;
    })
    .AddTransient(provider =>
    {
        var server = provider.GetService<Service>();
        return server.SubService2;
    })
    .AddTransient(provider =>
    {
        var server = provider.GetService<Service>();
        return server.SubService3;
    });

but this has the obvious problem of not awaiting the InitTask of the main service.

Then (since the sub-services are exposed via interfaces) I tried to code wrapper classes for the sub-services like

public class WrapperSubService1 : ISubService1
{
    private readonly Service server;

    public WrapperSubService1(Service server)
    {
        this.server = server;
    }

    private async ValueTask<ISubService1> GetSubService1Async()
    {
        await server.InitTask
        return server.SubService1;
    }

    // interface implementations

    public async Task<Example> GetExampleAsync(...) 
    {
        var subService1 = await this.GetSubService1Async();

        return await subService1.GetExampleAsync(...);
    }

    // many more (also some events and properties)
}

and do at startup

services.AddTransient<Service>()
    .AddTransient<ISubService1, WrapperSubService1>()
    .AddTransient<ISubService2, WrapperSubService2>()
    .AddTransient<ISubService3, WrapperSubService3>();

but this has also an obvious flaw: code duplication.

What I would wish for would be something like:

services.AddTransient<Service>()
    .AddTransient(async provider =>
    {
        var server = provider.GetService<Service>();
        await server.InitTask;
        return server.SubService1;
    })
    .AddTransient(async provider =>
    {
        var server = provider.GetService<Service>();
        await server.InitTask;
        return server.SubService2;
    })
    .AddTransient(async provider =>
    {
        var server = provider.GetService<Service>();
        await server.InitTask;
        return server.SubService3;
    });

but this then exposes just Task<SubService1> for injection.

Any ideas?

like image 650
Ackdari Avatar asked Aug 04 '26 17:08

Ackdari


1 Answers

The only solutions I've found for this kind of situation is to either:

  1. Make your initialization synchronous. On ASP.NET in particular, I think this is a fine option, since it makes sense to not receive requests until the initialization is complete.
  2. Have a separate "schema deployer" project that does that kind of initialization (creating queues/tables) before your service is even deployed. This is the solution I tend to use in modern apps.
like image 78
Stephen Cleary Avatar answered Aug 06 '26 07:08

Stephen Cleary



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!