簡體   English   中英

使用 Asp.Net Core DI 的服務的配置范圍

[英]Configuration scopes for services with Asp.Net Core DI

是否可以為 Asp.Net Core 中的默認 DI 設置注入范圍? 我的意思是例如:

services.AddSingleton<IUser, UserService>
services.AddSingleton<IUser, UserService>

對於第二個配置,以某種方式指定它應該只注入 HomeController。 與第一個不同,應該注入所有其他人。 默認DI可以嗎?

我在這里回答了一個類似的問題,但使用了作用域而不是單例:

如何在 Asp.Net Core 中注冊同一接口的多個實現?

我的直覺是這可能是您想要實現的目標,或者可能是更好的方法,並且您可能將 User 與 UserService 混為一談。 當您有相同接口的多個實現時,DI 會將它們添加到一個集合中,因此可以使用typeof從集合中檢索您想要的版本。

// In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped(IUserService, UserServiceA);
    services.AddScoped(IUserService, UserServiceB);
    services.AddScoped(IUserService, UserServiceC);
}

// Any class that uses the service(s)
public class Consumer
{
    private readonly IEnumerable<IUserService> _myServices;

    public Consumer(IEnumerable<IUserService> myServices)
    {
        _myServices = myServices;
    }

    public UserServiceA()
    {
        var userServiceA = _myServices.FirstOrDefault(t => t.GetType() == typeof(UserServiceA));
        userServiceA.DoTheThing();
    }

    public UserServiceB()
    {
        var userServiceB = _myServices.FirstOrDefault(t => t.GetType() == typeof(UserServiceB));
        userServiceB.DoTheThing();
    }

    public UseServiceC()
    {
        var userServiceC = _myServices.FirstOrDefault(t => t.GetType() == typeof(UserServiceC));
        userServiceC.DoTheThing();
    }
}

假設這個注冊,依賴注入容器怎么可能知道哪個“單例”(當有兩個時它不是真正的單例)它應該注入到 HomeController 或不同的服務中,當它們IUser依賴於IUser

依賴項注冊為的類型,在您的情況下IUser ,是 DI 容器用於解析依賴項的“鍵”。 因此,兩個都依賴於IUser將以相同的方式解決它們的依賴關系。 對於單例生命周期,這意味着兩個服務獲得相同的實例。

服務注冊通常也會被替換。 因此,如果您有一個注冊AddSingleton<X, Y>() ,然后有另一個AddSingleton<X, Z>() ,那么后者將取代前者。 所以所有依賴於X服務都將收到Z

DI 容器,包括 ASP.NET Core 附帶的默認容器,通常支持通過依賴IEnumerable<X>來解析所有注冊。 但是對於這個例子,這只是意味着一個服務將同時獲得YZ

您正在尋找的最接近的東西是鍵控或命名依賴項。 雖然這些在某些DI 容器中受支持,但它們在技術上不是依賴注入的一部分,因此通常在許多容器(包括 ASP.NET Core 容器)中故意不存在 有關更多詳細信息以及解決此問題的一些想法,請參閱此答案


回到您的用例,您應該真正考慮一下您在那里實際做了什么。 如果您有兩個UserService “單例”實例,您應該真正思考為什么會這樣:為什么不是只有一個? 如果支持多個,為什么不將其注冊為瞬態?

更重要的是,這兩個實例之間可能有什么不同? 畢竟,它們都是同一個實現的實例,所以它們沒有太多可以做的不同。

如果您可以識別出這一點,並確認這確實使實例不同,那么也可以考慮在類型層次結構中將其拆分。 如果沒有用例,很難解釋這一點,但是您應該嘗試最終得到兩個 不同的接口,每個接口都完全滿足每個依賴服務類型的需求 所以HomeController可以依賴IUserA ,其他人可以依賴IUserB (請選擇比這更好的名稱)。

我有類似的問題。 有我的解決方案。

在 controller 的頂層,我使用自定義屬性進行操作,我需要特定的服務實現(例如報告):

 public class HomeController : ControllerBase
  {

    private readonly IService _service;

    public HomeController(IService service)
    {
      _service = service;
    }

    [HttpGet]
    [ReportScope]
    public IEnumerable<WeatherForecast> Get()
    {
      _service.DoSomeThing();
    }

該屬性由自定義中間件處理:

public class ReportScopeLoggingMiddleware
  {
    private readonly RequestDelegate _next;

    public ReportScopeLoggingMiddleware(RequestDelegate next)
    {
      _next = next;            
    }

    public async Task Invoke(HttpContext context, ReportScopeContext scopeContext)
    {        
      var controllerActionDescriptor = context
        .GetEndpoint()
        .Metadata
        .GetMetadata<ControllerActionDescriptor>();

      bool analytical = controllerActionDescriptor.EndpointMetadata.Any(m => m is ReportScopeAttribute);
      if (analytical) scopeContext.SetActive();
      
      await _next(context);
    }       
  }

在這個中間件中,我使用ReportScopeContext

  public class ReportScopeContext
  {
    public bool Active { get; private set; } = false;
    public void SetActive()
    {
      Active = true;
    }
    
  }

ReportScopeContext在 DI 中具有范圍生命周期,我將其用於 select 的 IService 實現:

  services.AddScoped<ReportScopeContext>();
  services.AddTransient<Service2>();
  services.AddTransient<Service1>();
  services.AddTransient<IService>(sp =>
    sp.GetRequiredService<ReportScopeContext>().Active
      ? sp.GetRequiredService<Service1>()
      : sp.GetRequiredService<Service2>());

我相信您可以為特定控制器指定特殊注入。

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

        services.AddScoped<TestService>();
        services.AddTransient(ctx =>
            new HomeController(new TestService("Non-default value")));
 }

教程有更深入的說明。

可以考慮解決的第二件事是注冊一個基於名稱的函數,該函數返回不同的服務實現。 我以前曾經這樣使用過。

暫無
暫無

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

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