如何为特定类注入不同的对象实现?
例如在团结中,我可以:
定义IRepository
container.RegisterType<IRepository, TestSuiteRepositor("TestSuiteRepository");
container.RegisterType<IRepository, BaseRepository>();
并致电所需的实施
public BaselineManager([Dependency("TestSuiteRepository")]IRepository repository)
答案 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开始,如果未能定义映射,则应该引发验证错误。