133

IServiceProvider is an interface with single method:

object GetService(Type serviceType);

It's used to create instances of types registered in .NET Core native DI container.

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.

I want to create an instance of IServiceProvider without having Setup method at all. I need it to resolve dependencies in an integration test assembly. Is it possible to get it at all in this scenario?

0

7 Answers 7

93

As goaty mentioned it's enough to create new ServiceCollection. Here's example class which can be used to access DI container in .NET Core:

public static class ServiceProviderFactory
{
    public static IServiceProvider ServiceProvider { get; }

    static ServiceProviderFactory()
    {
        HostingEnvironment env = new HostingEnvironment();
        env.ContentRootPath = Directory.GetCurrentDirectory();
        env.EnvironmentName = "Development";

        Startup startup = new Startup(env);
        ServiceCollection sc = new ServiceCollection();
        startup.ConfigureServices(sc);
        ServiceProvider = sc.BuildServiceProvider();
    }
}

Startup class is taken from tested project so the service registrations don't need to be repeated.

Then in test class simply use:

var foo = ServiceProviderFactory.ServiceProvider.GetServices(typeof(IFoo));
Sign up to request clarification or add additional context in comments.

7 Comments

It seems a bit annoying to me that I have to pass an IServiceProvider around. Microsoft should have at least made a static/singleton IServiceProvider.
@Gerry Microsoft didn't do that, because passing an instanced object around is backwards compatible with singletons -- but not the other way around. Additionally, many people misuse/abuse singletons, and its not good to encourage the use of something people abuse. If you want a static singleton service for your app, you can easily create your own singleton to encapsulate the IServiceProvider and then access it wherever you want via your own singleton. That's the backwards-compatibility part.
I don't think this is useful either. Here a new Startup instance is used to configure the ServiceCollection but this will contain different instances of any singletons used by the application, so again this does not provide access to a ServiceProvider that enables you to access the services configured for an application.
@Neutrino I think what you want to do is NOT quite what the OP here wants to do. But... if you want to instantiate a running instance of your app and then test it while also having access to the running app's service provider, then look into Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory. Or if you want to manually resolve a class of a given type within the app, but using explicit code to ask for the object rather than relying on the constructor injection chain, then look here: stackoverflow.com/a/32461714/795690 .
This is not a good approach for simple reason. As you build your own ServiceProvider, you no longer can ensure the registred singletons will be still singletons as there will be two service providers each providing their instances of singletons. learn.microsoft.com/en-us/aspnet/core/fundamentals/…
|
40

This is the default implementation of IServiceCollection from Microsoft: https://github.com/aspnet/DependencyInjection/blob/master/src/DI/ServiceCollection.cs

Looking at the code then you should be able to get an IServiceCollection simply by calling:

var serviceCollection = new Microsoft.Extensions.DependencyInjection.ServiceCollection();

Hope that helps :)

2 Comments

I don't see how that's useful. The new ServiceCollection is empty and won't contain any of the service registrations that would be created by Startup.ConfigureServices so the ServiceProvider built from it won't be capable of providing anything at all.
Quite useful actually @Neutrino - I built some extensions for configuration and need to test them
28

To get access to existing DI of ASP.NET Core application e.g. in some controller, you should just resolve it in a constructor. Example with some manager and workers:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
   services.AddMvc();

   services.AddSingleton<IFooManager, FooManager>();
   services.AddTransient<IFooWorker, FooWorker>();
}

Manually resolve workers for manager:

public class FooManager: IFooManager
{
    private readonly IServiceProvider _serviceProvider;

    public FooManager(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void Start()
    {
        var w1 = _serviceProvider.GetRequiredService<IFooWorker>();  // new instance of FooWorker
        var w2 = _serviceProvider.GetRequiredService<IFooWorker>();  // new instance of FooWorker
    }
}

Comments

19

First you need to install the Microsoft.Extensions.DependencyInjection NuGet package. (docs, API, API)

Then you create a new ServiceCollection and method chain it with the BuildServiceProvider method. In between that you can also register any service providers.

var serviceProvider = new ServiceCollection()
    .AddSingleton<IFooService, FooService>()
    .BuildServiceProvider();

Comments

8

Here is an updated approach:

var host = Host.CreateDefaultBuilder().ConfigureWebHostDefaults(builder =>
{
    builder.ConfigureAppConfiguration((hostingContext, config) =>
    {
        var env = hostingContext.HostingEnvironment;
        env.ContentRootPath = Directory.GetCurrentDirectory();
        env.EnvironmentName = "Development";
    });

    builder.UseStartup<Startup>();
}).Build();

Example usage:

host.Services.GetService<IFoo>();

Comments

4

use this ServiceProviderA = new ServiceCollection(). . . . . .BuildServiceProvider() .GetRequiredService<IServiceProvider>(); this ServiceProviderA contain itself

Comments

3

You can find it in Program.cs

public static IServiceProvider ServiceProvider { get; private set; }

public static void Main(string[] args)
{
    IHost build = CreateHostBuilder(args).Build();
    ServiceProvider = build.Services;
    build.Run();
}

2 Comments

You can do that. BUT, you should be really aware to NEVER use this ServiceProvider directly ... maybe only for singletons ... ALWAYS create a service scope from it. Without using a scope you'll be creating a memory leak, scoped and transient services will never be disposed as ServiceProvider keeps them in a dictionary.
follow up ... Internally ServiceProvider / ServiceProviderEngine / ServiceProviderEngineScope keeps object derived from IDisposable and IAsyncDisposable in a dictionary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.