使用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; } }