从 ConfigureServices 中使用 ASP.NET Core DI 解析实例

Resolving instances with ASP.NET Core DI from within ConfigureServices

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

设置容器非常简单:

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

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

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

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

IServiceCollection中没有这样的方法。

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

正在解决 Startup.ConfigureServices

中的依赖关系
public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    var serviceProvider = services.BuildServiceProvider();
    var service = serviceProvider.GetService<IMyService>();
}

正在解决 Startup.Configure

中的依赖关系
public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

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

    // ...
}

解决 ASP.NET Core 3 Startup.Configure 中的依赖关系

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

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属性分别。

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

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

还有一些方便的扩展方法可用,例如serviceProvider.GetService<IFooService>()(为Microsoft.Extensions.DependencyInjection添加一个using)。

在启动中解析服务class

注入依赖项

运行时的托管服务提供商可以将某些服务注入 Startup class 的构造函数,例如 IConfigurationIWebHostEnvironment (IHostingEnvironment in pre-3.0 versions), ILoggerFactoryIServiceProvider。请注意,后者是由托管层构建的实例,仅包含启动应用程序的基本服务

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 class 的构造函数中传递并直接使用 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);
}

手动解析服务(又名服务定位器)是 generally considered an anti-pattern。虽然它有其用例(对于框架 and/or 基础设施层),但您应该尽可能避免使用它。

如果您使用模板生成应用程序,您将在 Startup class:

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(配置服务)

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();
        }
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddDbContext<ConfigurationRepository>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));

    services.AddScoped<IConfigurationBL, ConfigurationBL>();
    services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}

可以通过这种方式在AuthorizeAttribute等属性中注入依赖

var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));

如果您只是为了将一个依赖项传递给您正在注册的另一个依赖项的构造函数而需要解析一个依赖项,则可以这样做。

假设您有一个接受字符串和 ISomeService 的服务。

public class AnotherService : IAnotherService
{
    public AnotherService(ISomeService someService, string serviceUrl)
    {
        ...
    }
}

当你在 Startup.cs 中注册时,你需要这样做:

services.AddScoped<IAnotherService>(ctx => 
      new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);

我知道这是一个老问题,但我很惊讶这里没有一个相当明显和令人作呕的 hack。

您可以利用定义自己的 ctor 函数的能力,在定义服务时从您的服务中获取必要的值...显然,每次请求服务时都会 运行,除非您明确 remove/clear 并在 exploiting ctor.

的第一个构造中重新添加此服务的定义

此方法的优点是不需要您在服务配置期间构建或使用服务树。您仍在定义服务的配置方式。

public void ConfigureServices(IServiceCollection services)
{
    //Prey this doesn't get GC'd or promote to a static class var
    string? somevalue = null;

    services.AddSingleton<IServiceINeedToUse, ServiceINeedToUse>(scope => {
         //create service you need
         var service = new ServiceINeedToUse(scope.GetService<IDependantService>())
         //get the values you need
         somevalue = somevalue ?? service.MyDirtyHack();
         //return the instance
         return service;
    });
    services.AddTransient<IOtherService, OtherService>(scope => {
         //Explicitly ensuring the ctor function above is called, and also showcasing why this is an anti-pattern.
         scope.GetService<IServiceINeedToUse>();
         //TODO: Clean up both the IServiceINeedToUse and IOtherService configuration here, then somehow rebuild the service tree.
         //Wow!
         return new OtherService(somevalue);
    });
}

修复此模式的方法是使 OtherService 显式依赖 IServiceINeedToUse,而不是隐式依赖它或其方法的 return 值...或以其他方式显式解决该依赖关系。

您可以通过这种方式使用 IApplicationBuilder 实例注入依赖项

public void Configure(IApplicationBuilder app)
    {
        //---------- Your code
        
        using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            var resultLogic = serviceScope.ServiceProvider.GetService<IResultLogic>();
            resultLogic.YourMethod();
        }           

        //---------- Your code
    }

public void ConfigureServices(IServiceCollection 服务) {

        services.AddSingleton<ISelfServiceConfigLoad, SelfServiceConfigLoader>();
        var sp = services.BuildServiceProvider();
        var configservice = sp.GetServices<ISelfServiceConfigLoad>();
        services.AddSingleton<IExtractor, ConfigExtractor>( sp =>
        {
            var con = sp.GetRequiredService<ISelfServiceConfigLoad>();
             var config = con.Load();
            return new ConfigExtractor(config.Result);
        });
        services.AddSingleton<IProcessor<EventMessage>, SelfServiceProcessor>();          
        services.AddTransient<ISolrPush, SolrDataPush>();
        services.AddSingleton<IAPICaller<string, string>, ApiRestCaller<string, string>>();
        services.AddSingleton<IDataRetriever<SelfServiceApiRequest, IDictionary<string, object>>, SelfServiceDataRetriever>();
       


    }