IServiceCollection接口用于构建依赖注入容器。在完全构建之后,它被组合成一个IServiceProvider实例,您可以使用该实例解析服务。可以将IServiceProvider注入到任何类中。IApplicationBuilder和HttpContext类也可以通过它们各自的ApplicationServices或RequestServices属性提供服务提供者。
IServiceProvider定义了一个GetService(Type类型)方法来解析服务:
var service = (IFooService)serviceProvider.GetService(typeof(IFooService));
还有一些方便的扩展方法可用,例如serviceProvider.GetService<IFooService>()(为Microsoft.Extensions.DependencyInjection添加一个using)。
解析启动类内部的服务
注入依赖关系
运行时的托管服务提供者可以将某些服务注入到Startup类的构造函数中,例如IConfiguration,
IWebHostEnvironment(3.0之前版本的IHostingEnvironment), ILoggerFactory和IServiceProvider。注意,后者是由宿主层构建的实例,仅包含启动应用程序所需的基本服务。
ConfigureServices()方法不允许注入服务,它只接受IServiceCollection参数。这是有意义的,因为ConfigureServices()是您注册应用程序所需服务的地方。但是你可以在这里使用注入到启动构造函数中的服务,例如:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Use Configuration here
}
在ConfigureServices()中注册的任何服务都可以注入到Configure()方法中;你可以在IApplicationBuilder参数后添加任意数量的服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IFooService>();
}
public void Configure(IApplicationBuilder app, IFooService fooService)
{
fooService.Bar();
}
手动解析依赖项
如果你需要手动解析服务,你最好使用Configure()方法中IApplicationBuilder提供的ApplicationServices:
public void Configure(IApplicationBuilder app)
{
var serviceProvider = app.ApplicationServices;
var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}
在Startup类的构造函数中传递并直接使用IServiceProvider是可能的,但如上所述,它将包含有限的服务子集,因此实用性有限:
public Startup(IServiceProvider serviceProvider)
{
var hostingEnv = serviceProvider.GetService<IWebHostEnvironment>();
}
如果必须在ConfigureServices()方法中解析服务,则需要不同的方法。你可以从IServiceCollection实例中构建一个中间的IServiceProvider,它包含了到目前为止已经注册的服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IFooService, FooService>();
// Build the intermediate service provider
var sp = services.BuildServiceProvider();
// This will succeed.
var fooService = sp.GetService<IFooService>();
// This will fail (return null), as IBarService hasn't been registered yet.
var barService = sp.GetService<IBarService>();
}
请注意:
通常应该避免在ConfigureServices()方法中解析服务,因为这实际上是配置应用程序服务的地方。有时你只需要访问IOptions<MyOptions>实例。你可以通过将IConfiguration实例的值绑定到MyOptions实例来实现这一点(这实际上是选项框架所做的事情):
public void ConfigureServices(IServiceCollection services)
{
var myOptions = new MyOptions();
Configuration.GetSection("SomeSection").Bind(myOptions);
}
或者重载AddSingleton/AddScoped/AddTransient:
// Works for AddScoped and AddTransient as well
services.AddSingleton<IBarService>(sp =>
{
var fooService = sp.GetRequiredService<IFooService>();
return new BarService(fooService);
}
手动解析服务(又名服务定位器)通常被认为是一种反模式。虽然它有它的用例(用于框架和/或基础设施层),但您应该尽可能避免使用它。