Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Allow acceptance of pre-created IServiceProvider instance #550

Closed
KallDrexx opened this issue Jan 5, 2016 · 7 comments
Closed

Allow acceptance of pre-created IServiceProvider instance #550

KallDrexx opened this issue Jan 5, 2016 · 7 comments
Milestone

Comments

@KallDrexx
Copy link

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.

@aarcarons
Copy link

Is there a timing for this? I have this exact use case, and I found the suggested solution will be of great help.

There are different evidences that this is a friction point for those who want to have full control of the building of the DI container while integrating ASP.NET Core as part of an application.

See:
http://stackoverflow.com/questions/38916620/asp-net-core-with-existing-ioc-container-environment
autofac/Autofac#811 (comment)

@kierenj
Copy link

kierenj commented Jul 3, 2017

In terms of implementation, would it be a case of having a configurable override for:

var hostingServiceProvider = hostingServices.BuildServiceProvider();

...and then deciding an API which is in harmony with the rest? E.g. builder.UseServiceProvider((IServiceCollection c) => MakeProviderFromCollection(c))?

Are there any landmines or complexities I'm blissfully unaware of, or is it worth me attempting a PR?

@davidfowl
Copy link
Member

Unfortunately, I don't think this is feasible as hosting needs to add services to something, the only reasonable thing we could do is make it so that hosting can optionally support providing an IServiceCollection.

@scottt732
Copy link

I realize this expands the scope of the request a bit but Autofac's solution to this problem is to allow you to create a child scope from an existing provider, allowing you to add additional resolutions to the child scope only. That would be the ideal solution here as I believe the goal is to get existing dependencies and scopes in, not to expose ASP.NET services to the root scope. This enables you to host multiple WebHosts with different sets of services without them interfering with each other.

@davidfowl
Copy link
Member

I realize this expands the scope of the request a bit but Autofac's solution to this problem is to allow you to create a child scope from an existing provider, allowing you to add additional resolutions to the child scope only

Our DI system has no feature like that and will likely never have a feature like that. The IServiceProvider isn't editable once it's made. You can only append to the IServiceCollection.

That would be the ideal solution here as I believe the goal is to get existing dependencies and scopes in, not to expose ASP.NET services to the root scope.

Scopes aren't really child containers, so it doesn't have the same effect. We used to do container chaining way back when, but it was just too hard to support in all of the other DI containers and it was removed.

This enables you to host multiple WebHosts with different sets of services without them interfering with each other.

That's already the case today, you just don't get to control the starting point, as we always make a collection for you.

@ENikS
Copy link
Member

ENikS commented Feb 5, 2018

@davidfowl Should this be closed by #1325 ?

@davidfowl
Copy link
Member

This should probably be closed in general for the reasons I stated above. We've added support to allow overriding the host service provider. If you don't consume the IServiceCollection that was passed in, things won't work so you still can't just pass an IServiceProvider ignoring everything else.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants