一, 什么是依赖注入
首先在 ASP.NET core 中是支持依赖注入软件设计模式, 或者说依赖注入是 ASP.NET core 的核心;
依赖注入 (DI) 和控制反转 (IoC) 基本是一个意思, 因为说起来谁都离不开谁; 或者可以说他们是同一个概念的不同角度描述;
软件设计原则中有一个依赖倒置原则(DIP), 就是为了解耦; 高层模块不应该依赖于底层模块. 二者都应该依赖于抽象; 抽象不应该依赖于细节, 细节应该依赖于抽象; 而依赖注入是实现这种原则的方式之一;
举个现实中例子: 小明去行政领一节 5 号电池, 然后行政给了小明一节黑象牌 5 号电池来分析 ;
小明只需要向行政领一节 5 号电池即可, 小明不需要关心什么牌子的电池, 电池从哪来的, 电池的价格等等. 他们俩共同需要关心的是一节 5 号电池即可;
即使后期行政给了小明北孚电池, 小明仍可以正常使用; 他们只需要满足一个规则 (5 号电池) 即可;
小明 (高层模块) 不应该依赖黑象牌电池(低层模块), 两者应该都依赖 5 号电池(抽象).
如果小明直接获取到 (new) 黑象牌电池, 如果后期业务变更提供的是北孚电池, 那么我们就需要更改小明的代码; 再如果公司有几百个小明, 代码量可想而知;
为了解决直接获取 (new) 黑象牌电池, 简单说为了解耦, 我们让每位员工通过行政领取(构造函数, 属性, 方法等), 这种即使更改其他品牌, 而小明压根不需要关心;
举个. Net core 中的例子:.Net core 中使用分布式缓存;
我们只需要在构造函数中获取 IDistributedCache, 然后就可以在方法中直接使用缓存, 我们不需要关心缓存的实现方式, 存储位置等等;
如果缓存从内存变成 Redis 或者 sqlserver, 甚至自己实现缓存, 而我们只需要在 ConfigureServices 中更改具体实现方式即可, 而不需要更改任何使用缓存的地方;
二, ASP.NET core 中依赖注入的生命周期
依赖注入的生命周期有三种 Transient,Scoped 和 Singleton;
1,Transient 每次调用都是不同的实例, 比如常用的 Microsoft.Extensions.Options.IConfigureOptions<T>;
2,Scoped 每次请求是同一个实例, 如 Entity Framework contexts;
3,Singleton 只有一个实例, 如 Microsoft.Extensions.Logging.ILogger<T>;
具体使用哪种, 要根据具体情况而定;
1, 比如我们一般的业务逻辑都是 Transient, 这个也是比较常用的;
2,Scoped 相对用的比较少, 当然也有很多业务逻辑也有用 Scoped 的; 当然他的妙用肯定是每次请求一个实例, 比如我们在系统中获取登录系统用户的 Id, 这时就可以用 Scoped, 不管在 Service 层或者 Repository 层等等, 获取的都是同一个用户;
3,Singleton 很多都是系统级别设计用单利, 比如日志;
三, 在 ASP.NET core 中使用依赖注入
基础业务逻辑代码, 获取用户列表
- public interface IUserInfoService
- {
- IEnumerable<UserInfo> GetUserInfo();
- }
- public class UserInfoService : IUserInfoService
- {
- public IEnumerable<UserInfo> GetUserInfo()
- {
- // 模拟 db 获取数据
- return new List<UserInfo> { new UserInfo { Id = 1, Name = "Emrys" }, new UserInfo { Id = 2, Name = "梅林" } };
- }
- }
- public class UserInfoMongoService : IUserInfoService
- {
- public IEnumerable<UserInfo> GetUserInfo()
- {
- // 模拟 MongoDB 获取数据
- return new List<UserInfo> { new UserInfo { Id = 1, Name = "Emrys" }, new UserInfo { Id = 2, Name = "梅林" } };
- }
- }
- public class UserInfo
- {
- public int Id { get; set; }
- public string Name { get; set; }
- }
1, 传统方式
- public class ValuesController : ControllerBase
- {
- IUserInfoService _userInfoService = new UserInfoService();
- [HttpGet]
- public IEnumerable<UserInfo> Get()
- {
- return _userInfoService.GetUserInfo();
- }
- }
在传统方式中, 获取用户的服务类直接用 new 的方式, 这也是很多初学者或者很多老手最经常使用的方式; 从中可以发现代码耦合度太高, 非常不利于维护, 在所有使用到 IUserInfoService 的地方都要 new 出对象;
如果后期需求变更, 需要替换 IUserInfoService 的实现, 比如从 MongoDB 中获取数据(现实示例中, 从黑象牌变成北孚电池), 那么就需要在所有 new 出 UserInfoService 的地方更改代码换成 UserInfoMongoService,IUserInfoService _userInfoService = new UserInfoMongoService();
我们如果需要 new 的对象需要实现单例模式 (Singleton), 每次请求 new 一个对象(Scoped) 模式, 那么还要另写代码实现;
2, 依赖注入方式
1, 在 Startup 类的 ConfigureServices 方法中设置注入
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddTransient<IUserInfoService, UserInfoService>();
- services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
- }
2, 在构造函数中获取实例
- public class ValuesController : ControllerBase
- {
- IUserInfoService _userInfoService;
- public ValuesController(IUserInfoService userInfoService)
- {
- _userInfoService = userInfoService;
- }
- [HttpGet]
- public IEnumerable<UserInfo> Get()
- {
- return _userInfoService.GetUserInfo();
- }
- }
在使用依赖注入方式时, 解决了传统方式耦合度, 如果后期变更实现, 只要在 services.AddTransient<IUserInfoService, UserInfoService>(); 变更成 UserInfoMongoService 即可;
在所有使用 IUserInfoService 的地方无须做任何改动; 而且可以非常简单的设置生命周期(Transient,Scoped,Singleton);
四, 总结
1, 设置注入和获取注入的方式不止一种, 示例只是演示了最简单和最常用的使用方式, 其他方式可以参考文档;
2, 可以替换. net core 中的默认注入容器, 如常用的 autofac, 可以实现更强大的功能; 详情参考 https://autofac.org/ ; 其他容器可以参考
3, 可以直接在 View 中获取注入 @inject IUserInfoService userInfoService
4, 可以在 httpcontext 里直接获取注入 HttpContext.RequestServices.GetService<IUserInfoService >();
5,Startup 中的 ConfigureServices 方法就是为了设置注入而存在的;
记得推荐 ^_^
来源: https://www.cnblogs.com/emrys5/p/aspnetcoredi.html