使用dependency injection注入多个实现

我目前正在开发ASP.NET核心项目,并希望使用内置的dependency injection(DI)function。

好吧,我从界面开始:

ICar { string Drive(); } 

并希望多次实现ICar界面

 public class BMW : ICar { public string Drive(){...}; } public class Jaguar : ICar { public string Drive(){...}; } 

并在Startup类中添加以下内容

 public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); services.AddTransient(); // or services.AddTransient(); } 

现在我必须在两个实现之间做出决定,我决定的类将在每个需要ICar实现的构造函数中设置。 但我的想法是,如果请求的控制器是BMWController,那么如果请求JaguarController,则使用BMW实现或使用Jaguar

DI对我来说没有意义。 我该如何正确处理这个问题?

为了更好地理解我的问题,请查看此图片: https : //media-www-asp.azureedge.net/media/44907/dependency-injection-golf.png ?raw = true如何依赖解析器工作以及在哪里可以我在ASP.NET Core中设置它?

在Unity中,可以制作类似于container.RegisterType("Male"); container.RegisterType("Female");

并调用这样的正确类型

 [Dependency("Male")]IPerson malePerson 

您正在寻找的function并不容易实现,至少在控制器中使用它时因为控制器有点特殊处理(默认情况下,控制器没有在ServiceCollection注册,因此没有被容器解析/实例化而在请求期间由ASP.NET Core实例化,另请参阅我的相关答案的解释和示例)。

使用内置的IoC容器,您只能通过工厂方法来完成,这里有一个BmwCarFactory类的示例:

 services.AddScoped(); services.AddScoped(); services.AddScoped(p => new BmwCarFactory(p.GetRequiredService()))); 

默认的IoC容器有意保持简单,提供dependency injection的基础知识,以帮助您入门,并使其他IoC容器能够轻松插入并替换默认实现。

对于更高级的场景,鼓励用户使用他们选择的IoC,它支持更高级的function(汇编扫描,装饰器,条件/参数化依赖等)。

AutoFac(我在我的项目中使用)支持这样的高级场景。 在AutoFac文档中有4个场景(与评论中建议的@pwas的第3个场景一起):

1.重新设计你的课程

需要一些额外的开销来重构代码和类层次结构,但却大大简化了注入服务的消耗

2.更改注册

如果您不愿意或无法更改代码,则文档会在此处对其进行描述。

 // Attach resolved parameters to override Autofac's // lookup just on the ISender parameters. builder.RegisterType() .WithParameter( new ResolvedParameter( (pi, ctx) => pi.ParameterType == typeof(ISender), (pi, ctx) => ctx.Resolve())); builder.RegisterType(); .WithParameter( new ResolvedParameter( (pi, ctx) => pi.ParameterType == typeof(ISender), (pi, ctx) => ctx.Resolve())); var container = builder.Build(); 

3.使用键控服务( 这里 )

它与之前的2方法非常相似,但是基于键而不是具体类型来解析服务

4.使用元数据

这与3.非常相似,但您可以通过属性定义键。

像Unity这样的其他容器有一些特殊的属性,比如可以用来注释依赖DependencyAttribute

 public class BmwController : Controller { public BmwController([Dependency("Bmw")ICar car) { } } 

但是这个和Autofac的第四个选项使IoC容器泄漏到您的服务中,您应该考虑其他方法。

或者,您可以创建基于某些约定来解析服务的类和工厂。 例如ICarFactory

 public ICarFactory { ICar Create(string carType); } public CarFactory : ICarFactory { public IServiceProvider provider; public CarFactory(IServiceProvider provider) { this.provider = provider; } public ICar Create(string carType) { if(type==null) throw new ArgumentNullException(nameof(carType)); var fullQualifedName = $"MyProject.Business.Models.Cars.{carType}Car"; Type carType = Type.GetType(fullQualifedName); if(carType==null) throw new InvalidOperationException($"'{carType}' is not a valid car type."); ICar car = provider.GetService(carType); if(car==null) throw new InvalidOperationException($"Can't resolve '{carType.Fullname}'. Make sure it's registered with the IoC container."); return car; } } 

然后就像使用它一样

 public class BmwController : Controller { public ICarFactory carFactory; public BmwController(ICarFactory carFactory) { this.carFactory = carFactory; // Get the car ICar bmw = carFactory.Create("Bmw"); } } 

IServiceProvider的替代品

 // alternatively inject IEnumerable public CarFactory : ICarFactory { public IEnumerable cars; public CarFactory(IEnumerable cars) { this.cars = cars; } public ICar Create(string carType) { if(type==null) throw new ArgumentNullException(nameof(carType)); var carName = ${carType}Car"; var car = cars.Where(c => c.GetType().Name == carName).SingleOrDefault(); if(car==null) throw new InvalidOperationException($"Can't resolve '{carName}.'. Make sure it's registered with the IoC container."); return car; } }