[英]Dependency injection resolving by name
如何為特定類注入不同的對象實現?
比如在Unity中,我可以定義IRepository
兩個實現
container.RegisterType<IRepository, TestSuiteRepositor("TestSuiteRepository");
container.RegisterType<IRepository, BaseRepository>();
並調用所需的實現
public BaselineManager([Dependency("TestSuiteRepository")]IRepository repository)
除了@adem-caglin 的回答之外,我還想在這里發布一些我為基於名稱的注冊創建的可重用代碼。
更新現在它可以作為nuget 包使用。
為了注冊您的服務,您需要將以下代碼添加到您的Startup
類中:
services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();
services.AddByName<IService>()
.Add<ServiceA>("key1")
.Add<ServiceB>("key2")
.Add<ServiceC>("key3")
.Build();
然后你可以通過IServiceByNameFactory
接口使用它:
public AccountController(IServiceByNameFactory<IService> factory) {
_service = factory.GetByName("key2");
}
或者你可以使用工廠注冊來保持客戶端代碼干凈(我更喜歡)
_container.AddScoped<AccountController>(s => new AccountController(s.GetByName<IService>("key2")));
擴展的完整代碼在github 中。
正如@Tseng 所指出的,命名綁定沒有內置解決方案。 但是,使用工廠方法可能對您的情況有所幫助。 示例應如下所示:
創建存儲庫解析器:
public interface IRepositoryResolver
{
IRepository GetRepositoryByName(string name);
}
public class RepositoryResolver : IRepositoryResolver
{
private readonly IServiceProvider _serviceProvider;
public RepositoryResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IRepository GetRepositoryByName(string name)
{
if(name == "TestSuiteRepository")
return _serviceProvider.GetService<TestSuiteRepositor>();
//... other condition
else
return _serviceProvider.GetService<BaseRepositor>();
}
}
在ConfigureServices.cs
注冊所需的服務
services.AddSingleton<IRepositoryResolver, RepositoryResolver>();
services.AddTransient<TestSuiteRepository>();
services.AddTransient<BaseRepository>();
最后在任何類中使用它:
public class BaselineManager
{
private readonly IRepository _repository;
public BaselineManager(IRepositoryResolver repositoryResolver)
{
_repository = repositoryResolver.GetRepositoryByName("TestSuiteRepository");
}
}
您不能使用內置的 ASP.NET Core IoC 容器。
這是設計使然。 內置容器有意保持簡單且易於擴展,因此如果您需要更多功能,可以插入第三方容器。
您必須使用第三方容器來執行此操作,例如 Autofac(請參閱文檔)。
public BaselineManager([WithKey("TestSuiteRepository")]IRepository repository)
在閱讀了依賴注入的官方文檔后,我認為您不能這樣做。
但我的問題是:您是否同時需要這兩個實現? 因為如果你不這樣做,你可以通過環境變量創建多個環境,並根據當前環境在Startup
類中具有特定功能,甚至可以創建多個Startup{EnvironmentName}
類。
當 ASP.NET Core 應用程序啟動時,
Startup
類用於引導應用程序、加載其配置設置等(了解有關 ASP.NET 啟動的更多信息)。 但是,如果存在名為Startup{EnvironmentName}
(例如StartupDevelopment
),並且ASPNETCORE_ENVIRONMENT
環境變量與該名稱匹配,則使用該Startup
類。 因此,您可以為開發配置Startup
,但有一個單獨的StartupProduction
,當應用程序在生產中運行時將使用它。 反之亦然。
我還寫了一篇關於從 JSON 文件注入依賴項的文章,這樣您就不必每次在實現之間切換時都重新編譯整個應用程序。 基本上,您保留一個帶有以下服務的 JSON 數組:
"services": [
{
"serviceType": "ITest",
"implementationType": "Test",
"lifetime": "Transient"
}
]
然后您可以在此文件中修改所需的實現,而不必重新編譯或更改環境變量。
希望這有幫助!
首先,這可能仍然是一個壞主意。 您試圖實現的是如何使用依賴項和如何定義它們之間的分離。 但是你想使用依賴注入框架,而不是反對它。 避免服務定位器反模式的可發現性差。 為什么不以類似於ILogger<T>
/ IOptions<T>
方式使用泛型?
public BaselineManager(RepositoryMapping<BaselineManager> repository){
_repository = repository.Repository;
}
public class RepositoryMapping<T>{
private IServiceProvider _provider;
private Type _implementationType;
public RepositoryMapping(IServiceProvider provider, Type implementationType){
_provider = provider;
_implementationType = implementationType;
}
public IRepository Repository => (IRepository)_provider.GetService(_implementationType);
}
public static IServiceCollection MapRepository<T,R>(this IServiceCollection services) where R : IRepository =>
services.AddTransient(p => new RepositoryMapping<T>(p, typeof(R)));
services.AddScoped<BaselineManager>();
services.MapRepository<BaselineManager, BaseRepository>();
從 .net core 3 開始,如果您未能定義映射,則應引發驗證錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.