繁体   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