[英]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>
来解析所有注册。 但是对于这个例子,这只是意味着一个服务将同时获得Y
和Z
。
您正在寻找的最接近的东西是键控或命名依赖项。 虽然这些在某些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>());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.