Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to extend IServiceProvider during runtime

TLDR: Is it possible to modify the IServiceProvider after the Startup has ran?

I am running dll's (which implement a interface of me) during run-time. Therefore there's a file listener background job, which waits till the plugin-dll is dropped. Now I want to register classes of this dll to the dependency-injection system. Therefore I added IServiceCollection as a Singleton to the DI inside ConfigureServices to use inside another method.

In therefore I created a test-project and just tried to modify the ServiceCollection in the controller, because it was easier than stripping the background job down.

services.AddSingleton<IServiceCollection>(services);

So I added IServiceCollection to my controller to check if I can add a class to the DI after the Startup class has ran.

[Route("api/v1/test")]
public class TestController : Microsoft.AspNetCore.Mvc.Controller
{
  private readonly IServiceCollection _services;

  public TestController(IServiceCollection services)
  {
    _services = services;

    var myInterface = HttpContext.RequestServices.GetService<IMyInterface>();
    if (myInterface == null)
    {
      //check if dll exist and load it
      //....
      var implementation = new ForeignClassFromExternalDll();
      _services.AddSingleton<IMyInterface>(implementation);
    }
  }

  [HttpGet]
  public IActionResult Test()
  {
    var myInterface = HttpContext.RequestServices.GetService<IMyInterface>();
    return Json(myInterface.DoSomething());
  }
}

public interface IMyInterface { /* ... */ }

public class ForeignClassFromExternalDll : IMyInterface { /* ... */ }

The Service was successfully added to the IServiceCollection, but the change is not persisted yet to HttpContext.RequestServices even after multiple calls the service count increases each time but I don't get the reference by the IServiceProvider.

Now my question is: Is that possible to achieve and yes how. Or should I rather not do that?

like image 312
Maximilian Ast Avatar asked May 09 '19 10:05

Maximilian Ast


People also ask

When should I use IServiceProvider?

The IServiceProvider is responsible for resolving instances of types at runtime, as required by the application. These instances can be injected into other services resolved from the same dependency injection container. The ServiceProvider ensures that resolved services live for the expected lifetime.

How do you resolve IServiceCollection?

Resolve dependencies using IServiceProvider Once the container has been created, the IServiceCollection instance is composed into an IServiceProvider instance. You can use this instance to resolve services. You can inject an instance of type IServiceProvider into any method of a class.

How do I get an instance of IServiceProvider in .NET Core?

An instance of IServiceProvider itself can be obtained by calling a BuildServiceProvider method of an IServiceCollection. IServiceCollection is a parameter of ConfigureServices method in a Startup class. It seems to be magically called with an instance of IServiceCollection by the framework.

Do transient services get disposed?

Disposable transient services are captured by the container for disposal. This can turn into a memory leak if resolved from the top-level container.


1 Answers

Is it possible to modify the IServiceProvider after the Startup has ran?

Short answer: No.

Once IServiceCollection.BuildServiceProvider() has been invoked, any changes to the collection has no effect on the built provider.

Use a factory delegate to defer the loading of the external implementation but this has to be done at start up like the rest of registration.

services.AddSingleton<IMyInterface>(_ => {
    //check if dll exist and load it
    //....
    var implementation = new ForeignClassFromExternalDll();
    return implementation;
});

You can now explicitly inject your interface into the controller constructor

private readonly IMyInterface myInterface;

public MyController(IMyInterface myInterface) {
    this.myInterface = myInterface;
}

[HttpGet]
public IActionResult MyAction() {
    return Json(myInterface.DoSomething());
}

and the load dll logic will be invoked when that interface is being resolved as the controller is resolved.

like image 130
Nkosi Avatar answered Oct 13 '22 02:10

Nkosi