Change service implementation later in execution (in Microsoft.Extensions.DependencyInjection)

1.3k views Asked by At

I have a use-case (application with login and after that various pages with different functionalities to navigate through) where I register most of my services in the ConfigureServices method (like one would normally do) and hence before the actual (UI) execution of my program.

I have however some services that I only register as dummies (or not even at all) due to the fact that these should just be set after I've logged in to a web service. During this log-in process it is not only checked if the user is valid but also various configuration data is got after user validation. Some of this data is needed by itself (e.g. user currency) but others is used to determine which implementation of a service should be used. And even other data is used as an implementation itself; means I get an instance of an object (implementing a specific interface IMyInterface) via HTTP GET and want to register this instance as the implementation for IMyInterface.

This configuration data (or the respective services) should then be added back to my IServiceProvider so they can be injected into pages that'll be displayed later. And that's what my question is about:

Is there any way to add service implementations later in runtime after the IServiceProvider has already been built? Remember that if anyway possible I only want to use stuff from Microsoft.Extensions.DependencyInjection (IServiceCollection, IServiceProvider, etc.) and no third party container (I know this works for example with DryIoC). What would partially help is if I'd be able to add IOptions to the IServiceProvider later in execution but it's more likely that I'd need to add service implementations.

2

There are 2 answers

1
Slappywag On

IServiceCollection provides an extension method which allows for a factory method to create your services. So you could add a method something like this to your ConfigureServices method.

services.AddTransient<IMyService>((serviceProvider) =>
{
   // You can get services you need to decide what implementation to use like so
   var someDecidingService = serviceProvider.GetService<ISomeOtherService>();

   // you can use whatever logic you need to decide what implementation to use
   if (someDecidingService.UseImplementationA())
   {
      return new ImplementationA();
   }
   else
   {
     return new ImplementationB();
   }
});

Depending on your logic though it might make more sense to add a factory class to your container, which could encapsulate the logic used to decide which service implementation to use.

0
Natalia Muray On

I do not think it is possible directly, but you can do something like this

public void ConfigureServices(IServiceCollection services)
{
    // Register the all required implementations using their implementation name
    services.AddScoped<MyService1>();
    services.AddScoped<MyService2>();
    services.AddScoped<MyService3>();

    // Decide on the required implementation at runtime and return the correct one
    services.AddScoped<IMyService>(context => 
    {
        //logic here for deciding on the implementation
        if (useService1)
        {
            return context.GetRequiredService<MyService1>();
        }
        else if (userService2)
        {
            return context.GetRequiredService<MyService2>();
        }
        
        return context.GetRequiredService<MyService3>();
    });
}