在 ConfigureServices 中使用 ASP.NET Core DI 解析实例 [英] Resolving instances with ASP.NET Core DI from within ConfigureServices

查看:29
本文介绍了在 ConfigureServices 中使用 ASP.NET Core DI 解析实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用 ASP.NET Core MVC 内置依赖注入框架手动解析类型?

设置容器很容易:

public void ConfigureServices(IServiceCollection services){//...services.AddTransient();}

但是如何在不执行注入的情况下解析 ISomeService ?例如,我想这样做:

ISomeService service = services.Resolve();

IServiceCollection 中没有这样的方法.

解决方案

IServiceCollection 接口用于构建依赖注入容器.完全构建后,它会组合成一个 IServiceProvider 可用于解析服务的实例.您可以将 IServiceProvider 注入任何类.IApplicationBuilderHttpContext 类也可以通过它们的 ApplicationServicesRequestServices 属性.

IServiceProvider 定义了一个 GetService(Type type) 解析服务的方法:

var service = (IFooService)serviceProvider.GetService(typeof(IFooService));

还有几种方便的扩展方法可用,例如 serviceProvider.GetService()(为Microsoft.Extensions.DependencyInjection添加using).

在启动类中解析服务

注入依赖

运行时的托管服务提供者可以将某些服务注入到Startup 类的构造函数中,例如IConfiguration,IWebHostEnvironment (IHostingEnvironment 在 3.0 之前的版本中),ILoggerFactoryIServiceProvider.请注意,后者是由托管层构建的实例,仅包含启动应用程序的基本服务.

ConfigureServices() 方法不允许注入服务,它只接受一个 IServiceCollection 参数.这是有道理的,因为 ConfigureServices() 是您注册应用程序所需服务的地方.但是,您可以在此处使用在启动构造函数中注入的服务,例如:

public Startup(IConfiguration配置){配置=配置;}公共 IConfiguration 配置 { 获取;}public void ConfigureServices(IServiceCollection 服务){//这里使用配置}

任何在ConfigureServices()中注册的服务都可以注入到Configure()方法中;您可以在 IApplicationBuilder 参数后添加任意数量的服务:

public void ConfigureServices(IServiceCollection services){services.AddScoped();}公共无效配置(IApplicationBuilder 应用程序,IFooService fooService){fooService.Bar();}

手动解析依赖

如果需要手动解析服务,最好在Configure()方法中使用IApplicationBuilder提供的ApplicationServices:>

public void Configure(IApplicationBuilder app){var serviceProvider = app.ApplicationServices;var hostsEnv = serviceProvider.GetService();}

可以在Startup 类的构造函数中传递和直接使用IServiceProvider,但如上所述这将包含有限的服务子集,因此实用性有限:

public Startup(IServiceProvider serviceProvider){var hostsEnv = serviceProvider.GetService();}

如果您必须在 ConfigureServices() 方法中解析服务,则需要不同的方法.您可以从 IServiceCollection 实例构建一个中间 IServiceProvider 实例,该实例包含已注册到那时的服务:

public void ConfigureServices(IServiceCollection services){services.AddSingleton();//构建中间服务提供者var sp = services.BuildServiceProvider();//这会成功.var fooService = sp.GetService();//这将失败(返回 null),因为 IBarService 尚未注册.var barService = sp.GetService();}

请注意:通常,您应该避免在 ConfigureServices() 方法内解析服务,因为这实际上是您配置应用程序服务的地方.有时您只需要访问 IOptions 实例.您可以通过将 IConfiguration 实例中的值绑定到 MyOptions 的实例来实现这一点(这实际上是选项框架所做的):

public void ConfigureServices(IServiceCollection services){var myOptions = new MyOptions();Configuration.GetSection("SomeSection").Bind(myOptions);}

或者对 AddSingleton/AddScoped/AddTransient 使用重载:

//也适用于 AddScoped 和 AddTransientservices.AddSingleton(sp =>{var fooService = sp.GetRequiredService();返回新的 BarService(fooService);}

手动解析服务(又名服务定位器)是通常被认为是一种反模式.虽然它有其用例(用于框架和/或基础设施层),但您应该尽可能避免使用它.

How do I manually resolve a type using the ASP.NET Core MVC built-in dependency injection framework?

Setting up the container is easy enough:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<ISomeService, SomeConcreteService>();
}

But how can I resolve ISomeService without performing injection? For example, I want to do this:

ISomeService service = services.Resolve<ISomeService>();

There are no such methods in IServiceCollection.

解决方案

The IServiceCollection interface is used for building a dependency injection container. After it's fully built, it gets composed to an IServiceProvider instance which you can use to resolve services. You can inject an IServiceProvider into any class. The IApplicationBuilder and HttpContext classes can provide the service provider as well, via their ApplicationServices or RequestServices properties respectively.

IServiceProvider defines a GetService(Type type) method to resolve a service:

var service = (IFooService)serviceProvider.GetService(typeof(IFooService));

There are also several convenience extension methods available, such as serviceProvider.GetService<IFooService>() (add a using for Microsoft.Extensions.DependencyInjection).

Resolving services inside the startup class

Injecting dependencies

The runtime's hosting service provider can inject certain services into the constructor of the Startup class, such as IConfiguration, IWebHostEnvironment (IHostingEnvironment in pre-3.0 versions), ILoggerFactory and IServiceProvider. Note that the latter is an instance built by the hosting layer and contains only the essential services for starting up an application.

The ConfigureServices() method does not allow injecting services, it only accepts an IServiceCollection argument. This makes sense because ConfigureServices() is where you register the services required by your application. However you can use services injected in the startup's constructor here, for example:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    // Use Configuration here
}

Any services registered in ConfigureServices() can then be injected into the Configure() method; you can add an arbitrary number of services after the IApplicationBuilder parameter:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IFooService>();
}

public void Configure(IApplicationBuilder app, IFooService fooService)
{
    fooService.Bar();
}

Manually resolving dependencies

If you need to manually resolve services, you should preferably use the ApplicationServices provided by IApplicationBuilder in the Configure() method:

public void Configure(IApplicationBuilder app)
{
    var serviceProvider = app.ApplicationServices;
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}

It is possible to pass and directly use an IServiceProvider in the constructor of your Startup class, but as above this will contain a limited subset of services, and thus has limited utility:

public Startup(IServiceProvider serviceProvider)
{
    var hostingEnv = serviceProvider.GetService<IWebHostEnvironment>();
}

If you must resolve services in the ConfigureServices() method, a different approach is required. You can build an intermediate IServiceProvider from the IServiceCollection instance which contains the services which have been registered up to that point:

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

Please note: Generally you should avoid resolving services inside the ConfigureServices() method, as this is actually the place where you're configuring the application services. Sometimes you just need access to an IOptions<MyOptions> instance. You can accomplish this by binding the values from the IConfiguration instance to an instance of MyOptions (which is essentially what the options framework does):

public void ConfigureServices(IServiceCollection services)
{
    var myOptions = new MyOptions();
    Configuration.GetSection("SomeSection").Bind(myOptions);
}

Or use an overload for AddSingleton/AddScoped/AddTransient:

// Works for AddScoped and AddTransient as well
services.AddSingleton<IBarService>(sp =>
{
    var fooService = sp.GetRequiredService<IFooService>();
    return new BarService(fooService);
}

Manually resolving services (aka Service Locator) is generally considered an anti-pattern. While it has its use-cases (for frameworks and/or infrastructure layers), you should avoid it as much as possible.

这篇关于在 ConfigureServices 中使用 ASP.NET Core DI 解析实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆