Search code examples
.net.net-coregraphqlhotchocolate

Is there anyway to add services to the service provider at runtime? Or rebuild it? On .NET 5.0 or .NET core 3+


I have a multitenant application on a micro service architecture design.

I want to inject X number of services, depending on the number of tenants running.

 public void ConfigureServices(IServiceCollection services)
    {
        // ... OTHER DI

        services.AddHttpClient("TenantsService")
            .AddTypedClient<ITenantServiceClient>(c => new TenantServiceClient(new TenantServiceClientSettings()
            {
                AccessKey = Configuration["TenantsService:ApiKey"],
                BaseUrl = new Uri(Configuration["TenantsService:Url"])
            }, c)); 

        foreach (var tenant in TenantsToRegister)
        {
            services
                .AddGraphQLServer($"{tenant.Name}");
        }

        ...

    }

The above code would work if I had the list of tenants when the application starts. But I need to request that list from another microservice. Having this constraint, I need to build the service provider in order to get that list. At the same time, I need the list before the service provider's build to inject the services I need.

The only option that I see is adding the services at runtime, but I'm not sure if it's possible.


Solution

  • From a architectual point of view I would recommend to use "real" services for each tenant. For example start a docker or a process of your application per tenant. Like written in a former answer (thanks to sergey).

    If you really want to start a new GraphQLServer for each tenant in the same process then I would do it like this: Full example on dotnet fiddle

    services
        .AddHttpClient("TenantsService")
        .AddTypedClient<ITenantServiceClient>(c => 
                                        new TenantServiceClient(new TenantServiceClientSettings
                                        {
                                            AccessKey = "THE KEY", // Configuration["TenantsService:ApiKey"], 
                                            BaseUrl = new Uri("https://the-uri-you-need.com") // new Uri(Configuration["TenantsService:Url"])
                                        }, c));
            
    // build a temporary service provider before, with all services added until now.
    var tempServiceProvider = services.BuildServiceProvider();
    
    // resolve the tenant service and query for tenants
    var tenantsToRegister = tempServiceProvider.GetRequiredService<ITenantServiceClient>().GetTenants();
    
    // register needed tenants
    foreach (var tenant in tenantsToRegister)
    {
        services.AddGraphQLServer($"{tenant}");
    }
    

    ... of course the whole error handling around the call to the ITenantServiceClient must be added but its just for demo purpose. Here the special thing is the intermediate IServiceProvider which gives you the ability to use the full set of DI to query for your tenants but let you add the needed ones afterwards.

    BUT: This is also on startup and no additional servers can be added while runtime.

    Update Here is the updated dotnet fiddle (thanks to joao-figueira)