依赖注入按名称解析

时间:2016-08-22 05:37:02

标签: c# dependency-injection asp.net-core

如何为特定类注入不同的对象实现?

例如在团结中,我可以: 定义IRepository

的两个实现
container.RegisterType<IRepository, TestSuiteRepositor("TestSuiteRepository");
container.RegisterType<IRepository, BaseRepository>(); 

并致电所需的实施

public BaselineManager([Dependency("TestSuiteRepository")]IRepository repository)

5 个答案:

答案 0 :(得分:19)

正如@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");
    }
}

答案 1 :(得分:16)

除了@ adem-caglin回答之外,我还想在这里发布一些我为基于名称的注册创建的可重用代码。

更新现在它可用nuget package

要注册您的服务,您需要在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");
}

扩展程序的完整代码位于github

答案 2 :(得分:3)

您无法使用内置的ASP.NET Core IoC容器。

按设计。内置容器有意保持简单且易于扩展,因此如果您需要更多功能,可以插入第三方容器。

您必须使用第三方容器来执行此操作,例如Autofac(请参阅docs)。

public BaselineManager([WithKey("TestSuiteRepository")]IRepository repository)

答案 3 :(得分:2)

阅读the official documentation for dependency injection后,我认为你不能这样做。

但我的问题是:你是否同时需要这两个实现?因为如果不这样做,您可以create multiple environments through environment variables并拥有specific functionality in the Startup class based on the current environment.,甚至可以创建多个Startup{EnvironmentName}

  

当ASP.NET Core应用程序启动时,Startup类用于引导应用程序,加载其配置设置等(了解有关ASP.NET启动的更多信息)。但是,如果存在名为Startup{EnvironmentName}的类(例如StartupDevelopment),并且ASPNETCORE_ENVIRONMENT环境变量与该名称匹配,则使用该Startup类。因此,您可以配置Startup进行开发,但是在应用程序在生产环境中运行时会使用单独的StartupProduction。反之亦然。

我也wrote an article about injecting dependencies from a JSON file因此,每次要在实现之间切换时,都不必重新编译整个应用程序。基本上,您使用以下服务保留JSON数组:

"services": [
    {
        "serviceType": "ITest",
        "implementationType": "Test",
        "lifetime": "Transient"
    }
]

然后,您可以在此文件中修改所需的实现,而不必重新编译或更改环境变量。

希望这有帮助!

答案 4 :(得分:1)

首先,这可能仍然不是一个好主意。您要实现的目标是如何使用依赖项以及如何定义依赖项。但是您想使用依赖注入框架,而不是反对它。避免服务定位器反模式的可发现性差。为什么不以类似于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开始,如果未能定义映射,则应该引发验证错误。