簡體   English   中英

按名稱解析的依賴注入

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

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