-
Notifications
You must be signed in to change notification settings - Fork 308
Allow acceptance of pre-created IServiceProvider instance #550
Description
Currently Asp.Net 5 is solely in charge of instantiating an IServiceProvider instance to be used. While this works perfectly fine in Asp.Net websites, this is not ideal in circumstances where you are embedding Asp.Net 5 in another application (service or console application for example). For example, a console application that is running in the background might use Asp.Net 5 to expose http endpoints for easy communication with external systems (health checks, event notifications, etc...)
In this circumstance, Asp.Net is a relatively minor piece of infrastructure for the application as a whole but the current implementation of Asp.Net's dependency injection strategy requires that Asp.Net is the sole creator of the ioc container. This means that if you want a single ioc container for the whole application you must gather your service descriptors, pass them to Asp.Net, start the Asp.Net host, then call IWebApplication.ApplicationServices in order to retrieve the ioc container so it can be used in the non-Asp.net portions of the application. Without doing this, any service that's registered as an instance or singleton risks not working as expected (since each ioc container can now creates it's own instance).
This seems to me to cause my application to be too tightly coupled to Asp.Net for dependency injection, and means that if any other minor infrastructure makes the same assumption (requires it to be the creator of ioc containers) it becomes impossible to have consistent and sane dependency injection strategy.
It seems like we should at least have the option to allow calling classes to request services that Asp.Net needs to register, let the original class create the IServiceProvider instance, then pass it to the WebApplicationBuilder for usage.
An example of how this could look:
public class WebHost
{
private IHostingEngine _hostingEngine;
private IApplication _application;
public IServiceCollection GetServices()
{
// WebHostBuilder already has BuildHostedServices(). If this was decoupled from
// the call to execute the configureServices delegate inside then we should be able to
// somehow get a list of all services that Asp.Net wants to register by default
var services = WebHostBuilder.GetDefaultServices();
services.AddMvc();
return services;
}
public void Start(IServiceProvider serviceProvider)
{
_hostingEngine = new WebHostBuilder(GetConfig())
.UseServer("Microsoft.AspNet.Server.Kestrel")
.UseServiceProvider(serviceProvider)
.UseStartup<WebHost>()
.Build();
_application = _hostingEngine.Start();
}
}
........
public static void Main(string[] args)
{
var services = GetApplicationServices();
using (var host = new WebHost())
{
var webServices = host.GetServices();
foreach (var webService in webServices)
{
services.Add(webService);
}
var container = CreateIocContainer();
host.Start(container);
}
}
Then (theoretically) the WebHostBuilder.Build() checks if a IServiceProvider was given, if so then it ignores the building of services, gets the resolution it needs and passes it into WebApplication's constructor. I do see that WebApplication adds to the collection but that can also be abstracted away by an IWebApplication..GetRequiredServices() method.