[英]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.