How to get an instance of IServiceProvider in .NET Core?

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?

155210 次浏览

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

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

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();

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>();

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();
}

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
}
}

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