111

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?

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Arkadiusz Kałkus
  • 17,101
  • 19
  • 69
  • 108

7 Answers7

77

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));
Arkadiusz Kałkus
  • 17,101
  • 19
  • 69
  • 108
  • 1
    Great answer for showing you have to activate the ServiceCollection, build the provider, and then explicitly capture the reference to it. – Chris Marisic Oct 30 '18 at 19:34
  • 28
    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 Feb 07 '19 at 03:17
  • 8
    @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. – JamesHoux Sep 26 '19 at 14:04
  • 9
    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 Oct 28 '19 at 14:25
  • 3
    @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: https://stackoverflow.com/a/32461714/795690 . – MikeBeaton Nov 26 '19 at 16:55
  • 1
    This doesn't even compile in .NET Core? `cannot convert from 'Microsoft.Extensions.Hosting.Internal.HostingEnvironment' to 'Microsoft.Extensions.Configuration.IConfiguration'` – Rafael Feb 11 '20 at 01:10
  • 3
    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. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#ASP0000 – Andree Jan 28 '21 at 10:07
34

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 :)

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
  • 85
    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. – Neutrino Oct 28 '19 at 14:22
  • 4
    Quite useful actually @Neutrino - I built some extensions for configuration and need to test them – George Mauer Feb 21 '20 at 03:38
23

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
    }
}
Jabberwocky
  • 768
  • 7
  • 18
Ron
  • 415
  • 4
  • 8
10

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();
Fred
  • 12,086
  • 7
  • 60
  • 83
6

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>();
Pang
  • 9,564
  • 146
  • 81
  • 122
Nikolay
  • 1,076
  • 1
  • 13
  • 11
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();
}
Wouter
  • 2,540
  • 19
  • 31
  • 2
    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. – alex.pino Jul 11 '22 at 11:20
  • follow up ... Internally ServiceProvider / ServiceProviderEngine / ServiceProviderEngineScope keeps object derived from IDisposable and IAsyncDisposable in a dictionary. – alex.pino Jul 11 '22 at 11:47
2

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

Mu Jinming
  • 51
  • 5