为何需要依赖注入框架?
- 借助依赖注入框架,可轻松管理类之间的依赖,便于遵循设计原则,确保代码的可维护性和可扩展性
- ASP.NET Core的整个架构中,依赖注入框架提供了对象创建和生命周期管理的核心能力,各个组件相互协作,也是由依赖注入框架的能力来实现的。
依赖注入的组件包
依赖注入的核心包包括2个,一个是抽象包,一个是具体实现包,符合接口实现分离原则,便于调用。
- Microsoft.Extensions.DependencyInjection.Abstractions
- Microsoft.Extensions.DependencyInjection
核心类型
依赖注入框架包括以下的4个核心类型,并负责不同的分工
类型 | 内容 |
IServiceCollection | 负责服务的注册 |
IServiceDescriptor | 服务注册信息描述 |
IServiceProvider | 具体的容器,由IServiceCollection创建 |
IServiceScope | 表示容器的子容器的生命周期 |
生命周期
生命周期有3种,分别为Singleton、Scoped、Transient。
Singleton | 整个根容器内都是单例 |
Scoped | 在当前容器的生命周期内是单例的 |
Transient | 每次获取对象都是全新对象 |
服务的注册方式
在容器内注册服务可以有以下几种注册方式
- 正常注册
services.AddSingleton<IMySingletonService, MySingletonService>(); services.AddScoped<IMyScopedService, MyScopedService>(); services.AddTransient<IMYTransientService, MyTransientService>();
- 花式注册(包含实例注入、工厂模式)
//直接注入实例 services.AddSingleton<IOrderService>(new OrderService()); //工厂注册 services.AddSingleton<IOrderService>(factory => { return new OrderServiceEx(); });//可以注入,实现不同
- 尝试注册
尝试注册分为2种,一种是TryAdd...形式,一种是TryAddEnumerable形式
其区别是
- 1.TryAdd...形式只能注册同一类型的一个服务,即使实现不同也不能注入
- 2.TryAddEnumerable可注册相同类型的不同实现
//TryAdd...,相同类型的不会重复注入 services.TryAddSingleton<IOrderService, OrderService>();//不会注入,相同类型的都不会注入 services.TryAddSingleton<IOrderService, OrderServiceEx>();//不会注入,相同类型的都不会注入 //TryAddIEnumerable,可以注入不同实现 //services.TryAddEnumerable(services.AddSingleton<IOrderService, OrderService>());//注入报错,System.ArgumentException services.TryAddEnumerable(ServiceDescriptor.Singleton<IOrderService, OrderServiceEx>());//可以注入 //注入报错,System.ArgumentException //services.TryAddEnumerable(ServiceDescriptor.Singleton<IOrderService>(factory => //{ // return new OrderServiceEx(); //}));
由以上代码,我们可以看出,尝试注入相同的实现,会抛System.ArgumentException异常。
替换和移除
出去服务的注册,我们还需要了解服务的替换和移除方式,服务的替换有助于我们抛弃他人的定义来使用自己的定义
services.Replace(ServiceDescriptor.Singleton<IOrderService, OrderServiceEx>());//经测试,这里只能替换非尝试注册的服务 services.Remove(ServiceDescriptor.Singleton<IOrderService, OrderService>()); //services.RemoveAt(0); //services.RemoveAll<IOrderService>();
这里,我经过代码测试,发现,服务替换只能替换非尝试注册的方式,而尝试注册的方式不受影响,这里要注意一下,虽然还不知道原理是什么。
泛型注册
其实,不管是什么类型,注册方式都是类似的,常规注入方式需要提供2个类型,分别是接口定义和接口实现,那么一个泛型类型的定义可如下
services.AddSingleton(typeof(IGenericServie<>),typeof(GenericService<>));
获取依赖注入实例的2种方式
获取依赖注入实例有2种方式,且对应于不同的使用场景
- 构造函数注入适用于controller中大部分服务都需要用到的情况
- [FromService]标签,适用于服务仅有某个接口使用的情况
[FromService]可从容器中获取服务对象。
示例如下:
构造函数注入
public WeatherForecastController(ILogger<WeatherForecastController> logger,IOrderService orderService,IGenericServie<IOrderService> genericServie) { _logger = logger; }
[FromService]获取
[HttpGet] public int GetServiceList([FromServices] IEnumerable<IOrderService> orderServices) { foreach (var item in orderServices) { Console.WriteLine($"{item.GetType()}-{item.GetHashCode()}"); } return 1; }
到这里,本节就要结束了,下一节学习作用于与对象的释放行为。
源码地址
https://github.com/IronMarmot/Samples/tree/master/CoreSamples