簡體   English   中英

在 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中沒有這樣的方法。

IServiceCollection接口用於構建依賴注入容器。 完全構建后,它會組合成一個IServiceProvider實例,您可以使用它來解析服務。 您可以將IServiceProvider注入任何類。 IApplicationBuilderHttpContext類也可以分別通過它們的ApplicationServicesRequestServices屬性提供服務提供者。

IServiceProvider定義了一個GetService(Type type)方法來解析服務:

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

還有幾種方便的擴展方法可用,例如serviceProvider.GetService<IFooService>() (為Microsoft.Extensions.DependencyInjection添加一個using )。

解析啟動類中的服務

注入依賴

運行時的托管服務提供商可以注入一定的服務到的構造函數Startup類,如IConfigurationIWebHostEnvironmentIHostingEnvironment在3.0之前的版本), 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類的構造函數中傳遞並直接使用IServiceProvider ,但如上所述,這將包含有限的 services 子集,因此實用性有限:

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

如果您必須在ConfigureServices()方法中解析服務,則需要不同的方法。 你可以建立一個中間IServiceProviderIServiceCollection其中包含已注冊到該點的服務實例:

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實例來實現這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);
}

手動解析服務(又名服務定位器)通常被認為是一種反模式 雖然它有它的用例(用於框架和/或基礎設施層),但您應該盡可能避免它。

手動解析實例涉及使用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";

如果您使用模板生成一個應用程序,您將在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();
}

測試服務.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>();
}

家庭控制器.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();
        }

如果您只需要解析一個依賴項以將其傳遞給您正在注冊的另一個依賴項的構造函數,則可以執行此操作。

假設您有一個接受字符串和 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/")
);

您可以通過這種方式在 AuthorizeAttribute 等屬性中注入依賴項

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

我知道這是一個老問題,但我很驚訝這里沒有一個相當明顯和令人作嘔的黑客。

您可以利用定義自己的 ctor 函數的能力,在定義服務時從服務中獲取必要的值……顯然,除非您明確刪除/清除並重新添加服務的定義,否則每次請求服務時都會運行這項服務在開發 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 ,而不是隱式依賴它或其方法的返回值……或以其他方式顯式解析該依賴項。

您可以通過這種方式使用 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
    }

公共無效配置服務(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>();
       


    }
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM