.NET Core ASP.NET Core Basic 1-2
本节内容为控制反转与依赖注入
简介
控制反转 IoC
这个内容事实上在我们的 C# 高级篇就已经有所讲解, 控制反转是一种设计模式, 你可以这样理解控制反转, 假设有一个人他有一部 A 品牌手机, 他用手机进行听歌, 打游戏, 那么你可以创建一个手机类和一个人类
- class APhone : iPhone
- {
- public string Owner{get;set;}
- public Phone(string own)
- {
- Owner = own;
- }
- void Play()
- {
- // 省略
- }
- void Music()
- {
- // 省略
- }
- }
- class Man
- {
- public string Name{get;set;}
- void Game()
- {
- var p = new APhone(Name);
- p.Play();
- }
- }
事实上这段代码的耦合度是比较高的? 它使用的是正转, 也就是我需要什么东西的时候我就自己创建一个这个东西. 为什么说他不好呢, 如果有一天这个人决定再也不使用 A 品牌手机了, 他决定以后只使用 B 品牌. 那么也就意味着整个的 Man 类使用过 APhone 类的地方都需要更改. 这是一个非常麻烦的事情, 我们这个时候就需要运用我们的 IoC 控制反转了. 我们将实例或者是需要使用的对象的创建交给你的调用者, 自己只负责使用, 其它人丢给你依赖的这个过程理解为注入.
控制反转的核心就是 -- 原本我保存使用我自己的东西, 现在我把控制权交给我的上级, 我需要使用的时候再向他要. 这个时候, 接口的作用不言而喻, A 继承了 Phone 接口, B 也继承了, 假定我们一开始就使用 Phone 接口去创建不同的 A,B 对象, 那么是不是可以有效的切换 AB 对象呢?
依赖注入
依赖注入体现的是一个 IoC(控制反转), 它非常的简单, 我们之前的 Man 类代码中使用的是正转的方式, 也就是我要一个对象, 那么我就创建一个. 现在我们使用依赖注入就是将我们对这个对象的控制权交给上一级接口, 也就成为了这种, 我想要一个对象, 我就向上级发出请求, 上级就给我创建了一个对象. 我们通常使用构造函数注入的方式进行依赖的注入.
上文的代码就会变成
- class Man
- {
- private readonly iPhone _phone;
- public Man(iPhone phone)
- {
- _phone = phone;
- }
- }
假设这个时候你需要将手机换成 B 品牌, 那么只需要再注入的地方传入 B 品牌的对象即可了.
容器
但是现在又出现了一个新的问题, 假设说这个类有 100 个使用该接口的依赖, 如果, 我们是不是要在 100 个地方做这样的事情? 控制是反转了, 依赖的创建也移交到了外部. 现在的问题是依赖太多, 我们需要一个地方统一管理系统中所有的依赖, 这个时候, 我们就使用容器进行集中的管理
容器负责两件事情:
绑定服务与实例之间的关系
获取实例, 并对实例进行管理(创建与销毁)
使用
说了那么多, 我们如何在. NET Core 中使用我们的依赖注入呢? 这里我们针对的是所有的. NET Core 的应用, 在. NET Core 中依赖注入的核心分为两个组件: 位于 Microsoft.Extensions.DependencyInjection 命名空间下的 IServiceCollection 和 IServiceProvider.
其中
IServiceCollection 负责注册
IServiceProvider 负责提供实例
在默认的容器 ServiceCollection 中有三个方法
- .AddTransient<I,C>()
- .AddSingleton<I,C>()
- .AddScoped<I,C>()
这里就不得不提一下我们依赖注入的三种生命周期了
Singleton 指的是单例模式, 也就是说, 在整个程序运转期间只会生成一次
Transient 指每一次 GetService 都会创建一个新的实例
Scope 指在同一个 Scope 内只初始化一个实例 , 可以理解为( 每一个 request 级别只创建一个实例, 同一个 http request 会在一个 scope 内)
我们可以尝试使用控制台项目来模拟依赖注入的原理, 也就是说我们直接从容器获取我们对象实例, 并且我们使用 Guid 进行唯一性的标记.
- // 创建三个代表不同生命周期的接口
- interface IPhoneScope
- {
- Guid Guid { get; }
- }
- interface IPhoneSingleton
- {
- Guid Guid { get; }
- }
- interface IPhoneTransient
- {
- Guid Guid { get; }
- }
- // 实现的类
- class PhoneService:IPhoneScope,IPhoneSingleton,IPhoneTransient
- {
- public PhoneService()
- {
- this._guid = Guid.NewGuid();
- }
- public PhoneService(Guid guid)
- {
- this._guid = guid;
- }
- private Guid _guid;
- public Guid Guid => this._guid;
- }
然后, 在我们的主函数中
- namespace DI_AND_IOC
- {
- class Program
- {
- static void Main(string[] args)
- {
- // 注入服务
- var services = new ServiceCollection()
- .AddScoped<IPhoneScope, PhoneService>()
- .AddTransient<IPhoneTransient, PhoneService>()
- .AddSingleton<IPhoneSingleton, PhoneService>();
- // 构造服务
- var provider = services.BuildServiceProvider();
- using (var scope = provider.CreateScope())
- {
- var p = scope.ServiceProvider;
- var scopeobj1 = p.GetService<IPhoneScope>();
- var transient1 = p.GetService<IPhoneTransient>();
- var singleton1 = p.GetService<IPhoneSingleton>();
- var scopeobj2 = p.GetService<IPhoneScope>();
- var transient2 = p.GetService<IPhoneTransient>();
- var singleton2 = p.GetService<IPhoneSingleton>();
- Console.WriteLine(
- $"scope1: {scopeobj1.Guid},\n" +
- $"transient1: {transient1.Guid}, \n" +
- $"singleton1: {singleton1.Guid}\n");
- Console.WriteLine($"scope2: {scopeobj2.Guid}, \n" +
- $"transient2: {transient2.Guid},\n" +
- $"singleton2: {singleton2.Guid}\n");
- }
- // 创建不同的 scope
- using (var scope = provider.CreateScope())
- {
- var p = scope.ServiceProvider;
- var scopeobj3 = p.GetService<IPhoneScope>();
- var transient3 = p.GetService<IPhoneTransient>();
- var singleton3 = p.GetService<IPhoneSingleton>();
- Console.WriteLine($"scope3: {scopeobj3.Guid}, \n" +
- $"transient3: {transient3.Guid},\n" +
- $"singleton3: {singleton3.Guid}");
- }
- }
- }
- }
你应该会得到类似以下的数据
- scope1: 096d38e5-0c7b-4e50-9c79-241fb18a56ed,
- transient1: 289ebd11-8159-4f22-b53e-ed738a317313,
- singleton1: b453b7f5-3594-4b66-99c8-a72763abaa83
- scope2: 096d38e5-0c7b-4e50-9c79-241fb18a56ed,
- transient2: 212ad420-e54c-4dd6-9214-abe91aacdd9c,
- singleton2: b453b7f5-3594-4b66-99c8-a72763abaa83
- scope3: 688b6ffd-a8c1-47f4-a20a-872c2285d67c,
- transient3: 3d09997d-fffb-43d1-9e53-ccf9771c819d,
- singleton3: b453b7f5-3594-4b66-99c8-a72763abaa83
可以发现, singleton 对象是不会发生改变的, 而 scope 对象在创建新的 scope 之后就发生了改变, 而 transient 对象每一次请求都在发生改变.
需要注意的是, 在控制台项目使用容器服务需要引入 *** Microsoft.Extensions.DependencyInjection *** 程序集, 你可以在引入中导入该 dll
通过对注入服务的生命周期管控, 在一些 ASP.NET Core 项目中, 有些类 (服务) 有可能跨越了多个 Action 或者 Controller, 那么我们正确的使用生命周期, 我们可以尽可能的节省内存, 即能减少实例初始化的消耗.
在 ASP.NET Core 中的使用
在 ASP.NET Core 中, 我们使用依赖注入非常的简单, 在 StartUp 类中的 ConfigureServices 方法中已经为我们构建好了容器, 我们只需要做类似于这样的操作
- services.AddScoped<IPhoneScope, PhoneService>();
- services.AddDbContext<DbContext>();
- services.AddMVC();
如果你需要在控制器中注入服务, 官方的推荐方案是使用构造函数注入
- public IPhoneScope _ips;
- public Controller(IPhoneScope ips)
- {
- _ips = ips;
- }
特别的, 你如果使用 MVC 的 Razor 页面进行注入的话, 那么输入以下指令
@inject IPhoneScope ips
如果我的文章帮助了您, 请您在 GitHub.NETCoreGuide 项目帮我点一个 star, 在博客园中点一个关注和推荐.
GitHub https://github.com/StevenEco/.NetCoreGuide
BiliBili 主页 https://space.bilibili.com/33311288
WarrenRyan'sBlog https://blog.tity.xyz/
博客园
来源: https://www.cnblogs.com/WarrenRyan/p/11444398.html