aspnet/Hosting

Allow resolving Startup from external service provider

ENikS opened this issue · 9 comments

ENikS commented

When Unity container is configured to replace built in DI engine we discovered that it is not being used to create and resolve Startup class.

Currently WebHost builder is hard coded to create new built-in/internal service provider and resolve Startup class from that provider. Configured provider is ignored and created later. As result no types registered with Unity are available to the Startup constructor.

For more information please see this project

Probable duplicate of #1060

ENikS commented

@Tratcher No, it has nothing to do with collection

ENikS commented

As you can see on line 156 WebHostBuilder calls ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(serviceCollection) to get service provider. BuildServiceProvider simply creates new provider with no regards to configuration and registered factory:

public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }

    if (options == null)
    {
        throw new ArgumentNullException(nameof(options));
    }

    return new ServiceProvider(services, options);
}

I come to the same conclusion as @ENikS and also tried to relay this to @davidfowl at some point, but i failed to create a repro/ convey the problem.

https://twitter.com/pksorensen/status/851840194311487488

I’m not clear on what’s being asked for here. The WebHostBuilder cannot function with just an already built IServiceProvider, it needs to be an IServiceCollection (which is the issue that @Tratcher linked)

Ah I just read the other repository. You’re specifically talking about the hosting container itself (there’s always 2). Im not sure that’s possible with the way things are designed today. We would need to not DI activate the IServiceProviderFactory which it is today.

I’m not sure this is doable without hacks or breaking changes but we’d be open to looking at pull requests.

ENikS commented

The solution is rather simple. Requires changing of 3 lines of code. Sent you a PR

In #1060, @davidfowl you said "This isn't possible today. The WebHostBuilder is the root of the DI universe and you can't replace the built in IServiceCollection." I'm curious whether this is true for HostBuilder as well. I would assume if I'm adding AspNetCore and RabbitMq that there is some concept of isolated scopes between those two things? I gather this is the distinction between 1 hosting service provider and in this case 2 application service providers? Is it possible to have more than 1 AspNetCore runtime in a process w/isolated configuration/dependencies (i.e., UseKestrel(), UseHttpSys(), localhost:5000, localhost:5001)?

To be clear, I want to replace the container that gets a WebHost (Startable, Stoppable, Disposable thing). I don't need to/care to resolve MVC internals or the stuff supplied to ASP.NET Core via user-supplied code. If the WebHostBuilder/HostBuilder layer (which I presume is the 1 hosting service provider above) does allow us to create one with an existing DI container, what's the harm in services at that host layer from being made available to the application service providers (and not the other way around).

I'm not advocating for this lambda-type approach specifically, but something along these lines:
http://autofaccn.readthedocs.io/en/latest/lifetime/working-with-scopes.html#adding-registrations-to-a-lifetime-scope
In my mind, ideally container is for host-level services, the builder inside the lambda for application-specific services (AddMvc(), etc.). This would provide isolation between adjacent RabbitMQ/ASP.NET Core stacks with the exception of shared registrations from the host-level. This is impossible right now as far as I can tell b/c (a) Microsoft's DI library doesn't have this child scopes concept and because (b) the IServiceProviderFactory interface operates on an IServiceCollection and ContainerBuilder that the framework constructs for me. It won't allow me to bring my own.

Is this chicken/egg impossible or just the interfaces/code in place won't allow it? Like is it worth expending the effort to try and build a PR/workaround for or is this truly a dead-end? I have a truly miserable workaround after like dozens of hours of pain involving registering the root container as a backup Autofac registration source and letting the AutofacServiceProvider handle the 404-scenarios with a secondary lookup. It becomes really tricky to manage the lifetime of things and I do end up having to register a built container as a singleton in the ServiceCollection that WebHostBuilder insists on creating.

ENikS commented

@scottgu #1322 should solve your problem as well.