.NET Core 深入理解依赖注入 services.AddTransient,services.AddScoped,services.AddSingleton


1. 为什么要用依赖注入(DI)

什么是依赖注入,为什么要使用呢?简单通俗说就是一个类需要另一个类来协助工作,就产生了依赖,所以需要的依赖项就要【注入】过来一起来协同完成工作。 

软件设计原则中有一个依赖倒置原则(DIP)讲的是要依赖于(1)抽象,不要依赖于具体,(2)高层模块不应该依赖于低层模块, 二者应该依赖于抽象。简单的说就是为了更好的解耦。而控制反转(Ioc)就是这样的一个实现思路, 这个思路的其中一种实现方式就是依赖注入(DI)。

感觉有点绕, 举个栗子:老李是一个维修工, 现在要出任务去维修, 得先去申领个扳手。

.NET Core 深入理解依赖注入 services.add

李: "请给我一把可以拧7mm大小的六角螺丝的扳手.", 然后库管老张就从仓库里拿了一把这样的大力牌扳手给老李。

在这个例子中, 维修工老李只要告诉库管我要一个 "可以拧7mm大小的六角螺丝"的扳手即可, 他不用关心扳手的品牌和样式, 也不用采购扳手,更不用关心这个扳手是怎么来的。

而对于库管, 他只需提供满足这样规则的一个扳手即可, 不用去关心老李拿着这个扳手之后去干什么。所以老李和老张都只是关心"可以拧7mm大小的六角螺丝的"这个规则即可, 也就是说, 如果后期仓库里不再提供大力牌扳手, 而是提供了这样的大牛牌扳手, 无论换了什么牌子和样式, 只要仍满足这个规则, 老李仍然可以正常工作。它们定义了一个规则(比如接口IWrench7mm), 二者都依赖于这个规则, 然后仓库无论提供大力牌(WrenchDaLi : IWrench7mm)还是大牛牌(WrenchDaNiu : IWrench7mm), 都不影响正常工作.

这就是依赖倒置原则(DIP),  不依赖于具体(牌子),  高层模块(老李)不应该依赖于低层模块(大力牌扳手), 二者应该依赖于抽象(IWrench7mm:可以拧7mm大小的六角螺丝)。如果直接由老李去获取(new)大力牌扳手, 那么当业务改变要求采用大牛牌的时候, 我们就要去修改老李的代码。为了解耦, 在本例中我们只要在配置中让仓库由原来的提供大力牌改为提供大牛牌即可。老李要使用的时候, 可以通过注入(构造器、属性、方法)的方式, 将仓库提供的扳手实例提供给老李使用。

注:仓库继承老李提出的接口(7mm规则)抽象方法,将其对接口方法进行实现

2. 依赖注入理解

引入依赖注入的目的是为了解耦。说白了就是面向接口编程,通过调用接口的方法,而不直接实例化对象去调用。

这样做的好处就是如果添加了另一个实现类,不需要修改之前代码,只需要修改注入的地方将实现类替换。上面说的通过接口调用方法,实际上还是需要去实例化接口的实现类,只不过不需要我们手动new 构造实现类,而是交给如微软的DI、Autofac这些工具去构建实现类。我们只需要告诉它们,某个类是某个接口的实现类,当用到的时候,工具(比如,微软的DI)会自动通过构造函数实例化类。

3. 依赖的服务如何注入

打开Startup这个文件,  看一下里面的ConfigureServices方法。顾名思义,  这个方法是用来配置服务,系统默认已经添加了一些服务, 剩下的就是我们把自己需要的用的添加进去。参数为服务集合IServiceCollection对象,这种对象提供了AddSingleton、AddScoped和AddTransient 三种方法来添加服务,三种方法添加的服务的生命周期不一样。

实例:

添加一个名为DIDemo的.NET CORE MVC项目,在该项目下创建一个服务文件夹(Servers)

1)定义接口ICount

.NET Core 深入理解依赖注入 services.add

2)实现接口类Count

.NET Core 深入理解依赖注入 services.add

至此,服务(类)有了,那么如何能让这个服务为我们所用呢?或者说为我们服务呢?

3)把类(服务)在Startup文件中通过ConfigureServices方法注入服务。

.NET Core 中自带的DI容器,可以理解为StartUp.CS文件。——不准确

使用容器的好处,由容器来统一管理实例的创建和销毁,你只需要关心怎么用就行了,不需要关系怎么创建跟销毁。

当然容器创建的实例都是有生命周期的。三种创建方法创建的实例生命周期不一样。

  • Transient: 瞬态模式,每一次访问都会创建一个新的实例
  • Scoped: 域模式,在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)。对象在一次请求中是相同的,但在不同请求中是不同的。
  • Singleton :单例模式,整个应用程序生命周期以内只创建一个实例

此外,常用注入方式有三种。

C# 全选
public void ConfigureServices(IServiceCollection services)
{
	……
	//下面先以AddScopend方法阐述下常用的三种注入方式
	//1.最常用的注入方式,以接口形式暴露服务。下面2中方式意思一样
	//1.1 AddScopend后面是(),里面的接口和实现类必须套一层typeof
	services.AddScoped(typeof(ICount), typeof(Count));
	//1.2 AddScopend后面是<>,里面就直接写接口和实现类,当然最后有一个()
	services.AddScoped<ICount, Count>();
	//2.自己注入自己,以实现形式暴露服务
	services.AddScoped(typeof(Count));
	services.AddScoped<Count>();
	//3.需要传参的构造函数的类的注入(后面实例有应用讲解)
	// services.AddScoped(typeof(ICount), sp => { return new Count(参数); }) ;
	//services.AddScoped<ICount>(sp => { return new Count(参数);}) ;
	……
}

4)接下来分析演示三种注入方法的区别:

上面ConfigureServices方法中保留下面的瞬态模式

第1种:瞬态模式,每一次访问都会创建一个新的实例

C# 全选
services.AddTransient<ICount, Count>();

服务注入之后,我们就要用它。切换到控制器。那么如何能把服务实例注入到控制器中来呢?有属性注入构造方法注入方法注入。这里一般会用构造方法注入

C# 全选
public class HomeController : Controller
{
	private ICount _count;//方便本类其他方法的调用,所以定义一个私有字段来接收
	public HomeController(ICount count)//通过构造方法注入实例,ASP.NET CORE内置了依赖注入容器
	{
		_count = count;
	}
	//说明:请求到home控制器,自然调用home控制器的构造方法,构造方法中需要一个ICount类型的对象,它怎么来的呢?这就是因为.NET Core内置了依赖注入容器,这个时候就会到StartUp.cs文件中的ConfigureServices方法中去找相应的依赖,而在那里告诉了ICount由Count来实现( services.AddTransient<ICount, Count>();),所以这时会去调用Count 的构造方法实例化Count对象。
	//接下来就可以在控制器中使用_count
	public IActionResult Index()
	{
		int c = _count.MyCount();
		ViewBag.count = c;
		return View();
	}
}

前端展示

.NET Core 深入理解依赖注入 services.add

运行效果,不断刷新页面也总是0,因为瞬态模式注入的服务,每一次访问都会创建一个新的实例

.NET Core 深入理解依赖注入 services.add

上面ConfigureServices方法中改为下面的单例模式

第2种:单例模式,整个应用程序生命周期以内只创建一个实例

C# 全选
services.AddSingleton<ICount, Count>();

.NET Core 深入理解依赖注入 services.add

运行效果,不断刷新页面不断增加1.

继续把上面ConfigureServices方法中改为下面的域模式

第3种:域模式,在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)

C# 全选
services.AddScoped<ICount, Count>();

运行效果,不断刷新页面一直保持为0,因为每次刷新页面都是一个新的请求,所以总是0,在一个请求内产生的实例对象才是唯一。

改进下测试代码

C# 全选
public class HomeController : Controller
{
	private IServiceProvider _provider;
	private ICount _count;//方便本类其他方法的调用,所以定义一个私有字段来接收
	public HomeController(ICount count,IServiceProvider provider)//通过构造方法注入实例,ASP.NET CORE内置了依赖注入容器
	{
		_count = count;
		_provider = provider;
	}
	//接下来就可以在控制器中使用_count
	public IActionResult Index()
	{
		//int c = _count.MyCount();
		//ViewBag.count = c;
		//注意导入using Microsoft.Extensions.DependencyInjection;
		ICount count1 = _provider.GetService<ICount>();
		ICount count2 = _provider.GetService<ICount>();
		int c1=count1.MyCount();
		int c2 = count2.MyCount();
		ViewBag.c1 = c1;
		ViewBag.c2 = c2;
		//ICount counter1 = _provider.GetService<ICount>();
		//ICount counter2 = _provider.GetService<ICount>();
		//int c1 = counter1.Get();
		//int c2 = counter2.Get();
		return View();
	}
}

前端调整

.NET Core 深入理解依赖注入 services.add

测试发现c1、c2分别为0,1

但是每次刷新又重新为0,1

因为每次刷新页面都是一个新的请求,所以总是0,在一个请求内产生的实例对象才是唯一

如果还不明白,可以看我发的这个文章:https://blog.csdn.net/weixin_41372626/article/details/104870373 

 

 

 

版权声明:本文为YES开发框架网发布内容,转载请附上原文出处连接
管理员
上一篇:SSCMS源码研究
下一篇:.NET Core ResponseCache 浏览器缓存
评论列表

发表评论

评论内容
昵称:
验证码:
验证码
关联文章

.NET Core 深入理解依赖注入 services.AddTransient,services.AddScoped,services.AddSingleton
.Net Core依赖注入
.NET 高效依赖注入:使用 Lazy<T> 和工厂模式优化性能与内存占用
AP.NET Core获得注入管理器
Cannot resolve scoped service from root provider ASP.NET Core
ASP.NET Core 服务注入对比:IServiceProvider.GetService vs Lazy<T> 注入性能分析
深入理解js中的yield
深入理解 EF Core HierarchyId:高效管理树形数据的利器
.NET Core 复制nuget包依赖的dll到输出目录
C# 利用Autofac批量接口注入依赖【学习记录】
(五)React Ant Design Pro + .Net5 WebApi:后端环境搭建-Autofac注入+ 泛型仓储
.NET Core 自定义中间件 Middleware
未能加载文件或程序集“CefSharp.Core.dll”或它的某一个依赖项。
.NET Core ResponseCache 浏览器缓存
asp.net core 支持多种身份认证方式
ASP.NET Core开发者学习路线图
.NET Core 实现动态代理做AOP(面向切面编程)
.net core MVC页面源码文件中文被编码
ASP.NET Core官网教程,资料查找
C# ASP.NET Core开发学生信息管理系统(一)

热门标签
.NET Core .NET Reactor ag-grid AI发布 api安全 ASP.NET Core C#DLL加密 C#播放声音 C#代码混淆 C#代码加密 ChromeDriver Codex DateTime DBeaver devexpress devTool DLL混淆 edge.js EF EFCore Electron element-ui el-form el-table excel FastReport FileStream FolderBrowerDialog FolderSelectDialog form提交 git gridcontrol gridview input javascript json字符串 JS转换对象JSON jwt JWT授权 linq log Math MCP mitmproxy MVC MySQL Navicat netstat nginx node_modules NSwag Nuget Nuget镜像 number PowerShell pyinstaller python pythoncom python爬虫 python抓包 pywin32 redis Requests-html RestSharp Selenium sql SQL Server Swagger to-cms Visual Studio VSCode vue VueRouter vue路由 VUE页面通讯 Webpack Windows Windows服务 winform wmi xlrd yaml YESCMS YESWEB开发框架 白象 表单提交 播放声音 打开URL 代码混淆 弹窗提醒 端口占用 对象转换 分布式 公共字典 机器码 进程排查 静态资源 开发指南 路由参数 密钥 配置教程 配置文件 权限 人工智能 任务 任务调度 日期间隔 日志 日志记录 省市区 授权验证 数据库 四舍五入 文案 文件读取 文件夹选择 文件目录选择 问题排查 行政区域数据 页面通讯 中间件 CSharp 事务锁 工单系统 并发控制 重复提交 CMS Markdig Markdown markdown-it marked 技术选型 VS Code 开发工具 源代码管理 版本控制 Docker PostgreSQL 时区 部署排查 CMS架构 EF Core 主题系统 二次开发 插件系统 容器 运维命令 镜像清理 Linux NAS 远程挂载 飞牛 fnOS S/4HANA SAP GUI SAP HANA SAP R/3 SAP入门 SAP版本 ERP SAP SAP MM 库存管理 物料管理 采购管理 入门教程 SAP S/4HANA SPRO 企业结构 采购组织 MM01 物料主数据 物料类型 BP分组 业务伙伴 供应商主数据 ME41 RFQ 库存物料 采购流程 ME51 消耗性物料 科目分配 采购申请 AC03 ML81N 外部服务 服务主数据 Business Partner SAP培训 ME51N MM模块 Lean Services MM-SRV 外部服务采购 PIR 供应来源 采购主数据 采购信息记录 ME31K 框架协议 计划协议 采购合同 ME01 供应来源确定 货源清单 MEQ1 供应源确定 配额安排 配额评分 MD04 MD21 MRP 计划文件 需求计划 批量程序 MD01N MD02 MRP Live MD05 MM 物料计划 优化采购 供应源 采购订单 ME2A 供应商确认 采购监控 Flexible Workflow 凭证释放 采购审批 释放策略 实地盘点 物料凭证 货物移动 MIGO 收货 移动类型 已撤回 供应商退货 货物发出 STO 库存转储 转移过账 生产订单 预留 GR/IR MIRO 供应商发票 物流发票校验 OMR2 税码 FI PP SD 实操教程 MRBR OMR6 发票差异 交货成本 后续借记 MI01 实物盘点 盘点差异 公司代码 工厂 组织结构 OMS2 主数据定制 自动科目确定 BP角色 CVI 伙伴确定 编号范围 凭证类型 字段选择 FBN1 OMBT OMC2 会计凭证 OMJJ BOM 委外加工 项目类别L MRKO 供应商寄售 特殊库存K MRKON PIPE Pipeline 特殊库存P ERS MRIS 发票计划 周期性结算 里程碑付款 变更追踪 版本管理 采购凭证 SFTP WebDAV 网盘 飞牛fnOS AMPL HERS MPN 中文教程 库存确定 可用性检查 缺件检查 Output Management 消息确定 输出确定 分割评估 库存计价 评估类别 评估类型 PB00 RM0000 条件技术 采购定价 MM-FI集成 OBYC 库存估价 文本类型 文本采用 EFB EVO MSV SU3 用户参数 发票校验 合同参照 履约保留款 特别总账 预付款 Fiori Launchpad SAP Fiori 应用导航 用户体验 LSMW LTMC Migration Cockpit 数据迁移 BRFplus OPD Output Control My Inbox 审批流程 灵活工作流 SAP PP 外部加工 SAP QM 检验批 质量信息记录 采购收货 SAP PM 维护BOM 维护订单 SAP SD SAP Service 端到端流程 MM模块培训 FI-MM集成 供应商管理 审批配置 FICO入门 SAP FICO 财务配置 供应商税务 预扣税 House Bank 银行对账 客户清账 应收账款 FI控制 验证与替代 印度 GST 税务配置 F110 FBZP EWM入门 SAP EWM 仓库管理 OX14 成本核算 物料评估 后勤配置 物料组 价值更新 数量更新 PP-PI 流程制造 生产计划 容差配置 SAP事务码 SAP基础 TCODE Basis 事务代码 MMNR 编号区间 采购实操 组织架构 OMSF SAP实操 FI配置
联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码