提问者:小点点

从ConfigureReservices中解析具有ASP.NET核心DI的实例


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

设置容器非常容易:

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

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

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

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

IServiceCollection中没有这样的方法。


共3个答案

匿名用户

IServiceCollection接口用于构建依赖注入容器。在完全构建之后,它将被组合到一个IServiceProvider实例中,您可以使用该实例来解析服务。您可以将IServiceProvider注入到任何类中。IApplicationBuilderHttpContext类也可以分别通过它们的ApplicationServicesRequestServices属性提供服务提供者。

IServiceProvider定义用于解析服务的GetService(Type Type)方法:

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

还有几种方便的扩展方法可用,例如ServiceProvider.GetService()(使用为Microsoft.Extensions.DependencyInjection添加一个)。

运行库的宿主服务提供程序可以将某些服务注入Startup类的构造函数中,例如IconfigurationIWebHostEnvironment(IHostingEnvironment-3.0之前版本中的IloggerFactoryIServiceProvider。请注意,后者是由宿主层构建的实例,只包含启动应用程序所需的基本服务。

ConfigureReservices()方法不允许注入服务,它只接受IServiceCollection参数。这是有意义的,因为configureReservices()是注册应用程序所需服务的地方。但是,您可以在这里使用注入到初创企业的构造函数中的服务,例如:

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

public IConfiguration Configuration { get; }

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

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

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

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

如果需要手动解析服务,最好使用IApplicationBuilderConfigure()方法中提供的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>();
}

如果必须在configureReservices()方法中解析服务,则需要使用不同的方法。您可以从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>();
}

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

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

手动解析服务(又称服务定位器)通常被认为是一种反模式。虽然它有它的用例(对于框架和/或基础结构层),但是您应该尽可能地避免它。

匿名用户

手动解析实例涉及使用IServiceProvider接口:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    var serviceProvider = services.BuildServiceProvider();
    var service = serviceProvider.GetService<IMyService>();
}
public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

    // Using extension method.
    var service2 = serviceProvider.GetService<MyService>();

    // ...
}
public void Configure(
    IApplicationBuilder application,
    IWebHostEnvironment webHostEnvironment)
{
    application.ApplicationServices.GetService<MyService>();
}

某些类型可以作为方法参数注入:

public class Startup
{
    public Startup(
        IHostingEnvironment hostingEnvironment,
        ILoggerFactory loggerFactory)
    {
    }

    public void ConfigureServices(
        IServiceCollection services)
    {
    }

    public void Configure(
        IApplicationBuilder application,
        IHostingEnvironment hostingEnvironment,
        IServiceProvider serviceProvider,
        ILoggerFactory loggerfactory,
        IApplicationLifetime applicationLifetime)
    {
    }
}
[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";

匿名用户

如果您使用模板生成应用程序,则在startup类中将有如下内容:

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddMvc();
}

然后可以在那里添加依赖项,例如:

services.AddTransient<ITestService, TestService>();

如果要访问控制器上的ITestService,可以在构造函数上添加IServiceProvider,它将被注入:

public HomeController(IServiceProvider serviceProvider)

然后您可以解析您添加的服务:

var service = serviceProvider.GetService<ITestService>();

请注意,要使用泛型版本,必须将命名空间与扩展一起包含:

using Microsoft.Extensions.DependencyInjection;

iTestService.cs

public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

public class TestService : ITestService
{
    public int GenerateRandom()
    {
        return 4;
    }
}

startup.cs(ConfigureReservices)

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddMvc();

    services.AddTransient<ITestService, TestService>();
}

HomeController.cs

using Microsoft.Extensions.DependencyInjection;

namespace Core.Controllers
{
    public class HomeController : Controller
    {
        public HomeController(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService<ITestService>();
            int rnd = service.GenerateRandom();
        }