目录:
一, 什么是依赖注入
1.1, 什么是依赖?
1.2, 什么是注入?
1.3, 依赖注入解决的问题
二, 服务的生命周期 (.Net Core DI)
三, 替换默认服务容器
3.1, 为什么替换默认服务容器?
3.2, 如何替换服务容器
一, 什么是依赖注入
1, 什么是依赖
Rely 类
- public class Rely
- {
- public Task Test(string testMessage)
- {
- Console.WriteLine(testMessage);
- return Task.FromResult(0);
- }
- }
Output 类
- public class Output
- {
- Rely rely = new Rely();
- public async Task Out()
- {
- await rely.Test("這是一個測試消息");
- }
- }
Output 类需要 Rely 类来帮助它实现输出的功能, 这样 Output 类对 Rely 类产生了依赖, 可以理解为 Output 依赖于 Rely
依赖的一个设计原则: 依赖于抽象, 而不是具体的实现, 这个后面会具体解释的
2, 什么是注入
修改 Output 类
- public class Output
- {
- private Rely _rely;
- public Output(Rely rely)
- {
- _rely = rely;
- }
- public async Task Out()
- {
- await _rely.Test("這是一個測試消息");
- }
- }
在这里 Output 类不去实例化 Rely 类, 而是通过其他人传递给我, 我只用就好. 到底怎么理解注入呢?
简单来说就是别人对依赖创建实例化, 我自己只负责使用, 别人创建好了给我使用, 这么一个过程可以理解为注入
这里主要体现了控制反转 (IoC) 的思想, 什么是 IoC ? 我们看看下面的图就好理解了
直接依赖关系在运行的时候 A 调用 B,B 调用 C, 编译的时候 A 取决于 B,B 取决于 C.
而在反转依赖关系中, A 可以调用 B 实现的抽象上的方法, 让 A 可以在运行时调用 B, 而 B 又在编译时依赖于 A 控制的接口, 程序运行时流程跟直接依赖关系一样. 但是插入了接口意味着可以轻松的有不同实现
3, 依赖注入解决的问题
依赖注入主要体现了 IoC 思想, IoC 将实现详细信息编写为依赖并且实现了更高级的抽象, 因此程序测试性, 维护性, 模块化程度都更高了. 这也就对应了刚刚的那个设计规则 -- 依赖于抽象, 而不是具体的实现.
那么依赖注入到底解决了哪些问题呢?
问题一: 在直接依赖关系中如果 A 类需要更换为其他实现, 那么就必须得修改 B 类
问题二: 如果有多个依赖 B 类的类, 那么将会实例化多个配置, 这样代码会比较分散和冗余
问题三: 这种实现方法很难实现单元测试
解决这些问题的办法:
一: 使用了接口抽象话依赖关系的实现, 改动实现只需要改动注入的地方即可
二: 注册服务容器中的依赖关系, 有多处需要不许多出实例化配置, 直接在 Startup.ConfigureServices 中注册即可
二, 服务的生命周期 (.Net Core DI)
在. NET Core 中 DI 的核心分为两个组件: IServiceCollection 和 IServiceProvider.
IServiceCollection--- 负责注册
IServiceProvider--- 负责提供实例
在 Startup.cs 中 ConfigureServices 中注册服务
- public void ConfigureServices(IServiceCollection services)
- {
- services.Configure<CookiePolicyOptions>(options =>
- {
- // This lambda determines whether user consent for non-essential cookies is needed for a given request.
- options.CheckConsentNeeded = context => true;
- options.MinimumSameSitePolicy = SameSiteMode.None;
- });
- services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();// 单例生存期
- services.AddScoped<IHttpContextAccessor, HttpContextAccessor>();// 范围生存期
- services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();// 暂时生存期
- services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
- }
1, Transient(暂时生存期)-- 暂时生存期服务是每次从服务容器进行请求时创建的. 这种生存期适合轻量级, 无状态的服务.
暂时生存期会在每次请求的时候创建一个实例
2, Scoped(范围生存期)-- 范围生存期服务是每个客户端连接时创建的一次实例
范围生存期会在客户端连接时创建一次实例, 然后每次请求的实例都是相同的
3, Singleton(单例生存期)-- 单例生存期会在程序第一次请求是创建一次实例
单例生存期仅会在第一次连接时创建一次实例, 所有整个程序使用的实例都是同一个实例
三, 替换默认服务容器
1, 为什么替换默认服务容器
我们可以首先理解下什么是服务容器 - 依赖注入把依赖的创建给了别人, 别人创建好了再给我们使用. 那么在哪里创建依赖呢? 或者说在那里管理依赖呢? 这里就有了容器这个概念, 负责管理系统中所有的依赖.
那么我们为什么要替换容器呢?
内置的服务容器足够实现一些小型的项目或满足大多数的消费者, 但是遇到大型的项目就比较麻烦了, 依赖较多, 内置的服务容器就显得有点短板了. 当我们遇到这些问题的时候就可以考虑替换默认服务容器.
2, 如何替换服务容器
这里我们说下替换服务容器为 Autofac https://autofac.org/ .
安装适当的包
- Autofac
- Autofac.Extensions.DependencyInjection
在 Startup.ConfigureServices 中配置返回 为 IServiceProvider:
- public IServiceProvider ConfigureServices(IServiceCollection services)
- {
- services.AddMvc();
- // Add other framework services
- // Add Autofac
- var containerBuilder = new ContainerBuilder();
- containerBuilder.RegisterModule<DefaultModule>();
- containerBuilder.Populate(services);
- var container = containerBuilder.Build();
- return new AutofacServiceProvider(container);
- }
如果要使用第三方容器的话, Startup.ConfigureServices 必须返回 IServiceProvider.
然后我们在 DefaultModule 中配置 Autofac
- public class DefaultModule : Module
- {
- protected override void Load(ContainerBuilder builder)
- {
- builder.RegisterType<CharacterRepository>().As<ICharacterRepository>();
- }
- }
来源: https://www.cnblogs.com/hulizhong/p/10764332.html