实战Asp.Net Core:DI生命周期
title: 实战Asp.Net Core:DI生命周期
date: 2018-11-30 21:54:52
1、前言
Asp.Net Core 默认支持 DI(依赖注入) 软件设计模式,那使用 DI 的过程中,我们势必会接触到对象的生命周期,那么几种不同的对象生命周期到底是怎么样的呢?我们拿代码说话。
关于 DI 与 IOC:
个人理解:IOC(控制反转) 是目的(降低代码、服务间的耦合),而 DI 是达到该目的的一种手段(具体办法)。
2、DI生命周期
DI的生命周期,根据框架、库的不同,会略有差异。此处,我们就以微软的DI扩展为例,来说下DI中常用的几种生命周期。
首先,我们想象一个这样一个场景。假设我们有寄快递的需求,那么我们会致电快递公司:“我们要寄快递,派一个快递员过来收货”。接着,快递公司会如何做呢?
- 一直派遣同一个快递员来收货。
- 第一周派遣快递员A、第二周派遣快递员B收货。
- 每次都派遣一个新的快递员收货。
这对应到生命周期就是:
- 单例(Singleton),单一实例,每次使用都是该实例。
- 作用域实例(Scoped),在一个作用域(比如单次请求)内是同一个实例,不同的作用域实例不同。
- 瞬时实例(Transient),每次使用都创建新的实例。
快递公司也就是我们在DI中常说的容器(Container)了。
2.1、验证准备
首先,我们需要三个Services(Service1\Service2\Service3)内容一致,如下:
// Service1.cs,Service2、Service3除类名以外,内容一致
public class Service1
{
private int value = 0;
public int GetValue()
{
this.value++;
return this.value;
}
}
然后,我们需要一个业务类,再一次注入这三个Service,内容如下:
// DefaultBusiness.cs
public class DefaultBusiness
{
private readonly Service1 s1;
private readonly Service2 s2;
private readonly Service3 s3;
public DefaultBusiness(Service1 s1, Service2 s2, Service3 s3)
{
this.s1 = s1;
this.s2 = s2;
this.s3 = s3;
}
public int GetS1Value()
{
return this.s1.GetValue();
}
public int GetS2Value()
{
return this.s2.GetValue();
}
public int GetS3Value()
{
return this.s3.GetValue();
}
}
最后,还需要在Startup.cs进行注入
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// 单例模式
services.AddSingleton<Service1>();
// 作用域模式
services.AddScoped<Service2>();
// 瞬时模式
services.AddTransient<Service3>();
// 为了保证结果,将Business类注册为瞬时模式,每次注入都是全新的。
services.AddTransient<Business.DefaultBusiness>();
}
2.2、验证单例(Singleton)
对于单例来说,我们期望,在整个程序启动期间,都是同一个实例,所以,我们只需要在Service中,增加一个局部变量,做累加就可以验证。
// DefaultController.cs
[Route("singleton")]
public IActionResult SingletonTest()
{
this.defaultBiz.GetS1Value();
return Json(new
{
s1 = s1.GetValue()
});
}
然后我们访问 http://localhost:5000/singleton 多次,输入如下:
// 第一次
{ s1: 2 }
// 第二次
{ s1: 4 }
// 第三次
{ s1: 6 }
可以发现,每请求一次,value都会增加2。分析下怎么来的呢?
- 首先是执行
defaultBiz.GetValue()中,根据内部代码,此处会对注入的实例,value + 1。 - 之后,
Json()中的,s1 = s1.GetValue(),此处再一次增加了1。 - 综上,一次请求,s1 中的value值会增加2,由于是单例模式,在整个服务运行期间,都是一个实例,所以,每次请求都会累加2。
2.3、验证作用域实例(Scoped)
// DefaultController.cs
[Route("scoped")]
public IActionResult ScopedTest()
{
this.defaultBiz.GetS2Value();
return Json(new
{
s2 = s2.GetValue()
});
}
然后我们访问 http://localhost:5000/scoped 多次,输入如下:
// 第一次
{ s2: 2 }
// 第二次
{ s2: 2 }
// 第三次
{ s2: 2 }
从结果可以看出,每次请求的返回值是固定的,都为2,也就是证明了Service2中,value++执行了两次。对于执行value++的代码,只有 defaultBiz.GetS2Value() 和 s2 = s2.GetValue(),所以这两处操作的是同一个实例。这也就证明了,对于 Scoped 生命周期,在作用域(可以简单理解为单次请求,实际上并不准确,注意,此处为考虑多线程的情况)内,都是使用的同一个实例。在不同的请求之间,则是不同的实例。
2.4、验证瞬时实例(Transient)
// DefaultController.cs
[Route("transient")]
public IActionResult TransientTest()
{
return Json(new
{
s3 = s3.GetValue()
});
}
然后我们访问 http://localhost:5000/transient 多次,输入如下:
// 第一次
{ s3: 1 }
// 第二次
{ s3: 1 }
// 第三次
{ s3: 1 }
从结果来看,每次请求的都是相同的返回值,s3 = 1,这说明了,两次操作的value++,是针对的不同实例。也就是每次使用 Service1,都是全新的实例。
3、扩展(Autofac DI 类库)
Asp.Net Core中默认的DI,相对还是比较简单的,只有三个生命周期。对于时下比较的依赖注入库,一般都会有更多的生命周期,有些还会有生命周期事件可以监控。
以 Autofac 为例,该类库提供了如下一些生命周期,可以做到更精细化的控制:
- 单次依赖(Instance Per Dependency)- 也就是Transient,每次获取实例都是全新的。
- 单例(Single Instance) - 也就是单例,整个服务周期都是一个实例。
- 作用域隔离的实例(Instance Per Lifetime Scope) - 也就是一个作用域一个,示例代码如下:
// 先创建作用域
using(var scope1 = container.BeginLifetimeScope())
{
for(var i = 0; i < 100; i++)
{
// 在作用域内,Resolve 的都是同一个实例
var w1 = scope1.Resolve<Worker>();
}
}
// 创建另一个作用域
using(var scope2 = container.BeginLifetimeScope())
{
for(var i = 0; i < 100; i++)
{
// 在作用域内,Resolve 的都是同一个实例,但是这个实例和 scope1 作用域中的 w1 不是同一个。
var w2 = scope2.Resolve<Worker>();
}
}
- 带标签的作用域隔离实例(Instance Per Matching Lifetime Scope)
- 单次请求作用域实例(Instance Per Request) - 每个请求作为一个作用域。
- 指定Owner的作用域实例(Instance Per Owned)- 对于同一个Owner,实例保持一致
- 线程作用域实例(Thread Scope)
更多 Autofac 生命周期相关内容,请参考:https://autofac.readthedocs.io/en/latest/lifetime/instance-scope.html
4、总结
本文主要简单演示了 Asp.Net Core 中默认的几种服务生命周期效果,也抛砖引玉的说了下 Autofac 中的服务生命周期。合理的利用生命周期,可以减少对象的创建,提交程序性能。但是,用错了生命周期,则容易产生隐含的bug。在使用 DI 类库的时候,一定要理解清楚不同的生命周期的应用场景。
实战Asp.Net Core:DI生命周期的更多相关文章
- (3)ASP.NET Core 服务生命周期
1.前言 在ConfigureServices方法中的容器注册每个应用程序的服务,Asp.Core都可以为每个应用程序提供三种服务生命周期:●Transient(暂时):每次请求都会创建一个新的实例. ...
- 从Asp .net到Asp core (第二篇)《Asp Core 的生命周期》
前面一篇文章简单回顾了Asp .net的生命周期,也简单提到了Asp .net与Asp Core 的区别,我们说Asp Core不在使用Asp.netRuntime,所以它也没有了web程序生命周期中 ...
- ASP.NET Core DI 手动获取注入对象
ASP.NET Core DI 一般使用构造函数注入获取对象,比如在ConfigureServices配置注入后,通过下面方式获取: private IValueService _valueServi ...
- [转]ASP.NET应用程序生命周期趣谈(五) IIS7瞎说
Ps:建议初学者在阅读本文之前,先简要了解一下之前的几篇文章,以便于熟悉本文提到的一些关于IIS6的内容,方便理解.仅供参考. PS:为什么叫瞎说呢?我觉得自己理解的并不到位,只能是作为一个传声筒,希 ...
- [转]ASP.NET应用程序生命周期趣谈(四) HttpHandler和页面生命周期
在之前的三篇文章中,我们还算简明扼要的学习了asp.net的整个生命周期,我们知道了一个Request进来以后先去ISAPI Filter,发现是asp.net程序后又ASPNET_ISAPI.dll ...
- [转]ASP.NET应用程序生命周期趣谈(三) HttpModule
在之前的文章中,我们提到过P_Module(HttpModule)这个能干的程序员哥们儿,它通过在项目经理HttpApplication那里得到的授权,插手整个应用程序级别的事件处理.所有的HttpM ...
- [转]ASP.NET应用程序生命周期趣谈(一)
这几天一直在看ASP.NET应用程序生命周期,真是太难了,我理解起来费了劲了,但偏偏它又是那么重要,所以我希望能给大家带来一篇容易理解又好用的文章来帮助学习ASP.NET应用程序生命周期.这篇就是了. ...
- asp.net应用程序生命周期和asp.net网页的生命周期
一.asp.net应用程序生命周期 asp.net应用程序生命周期以浏览器向web服务器(比如IIS服务器)发送请求为起点,先后经历web服务器下的ISAPI(Internet Server Appl ...
- asp.net应用程序生命周期
asp.net应用程序生命周期(流程文字描述版) 请求——>IIS——>ISAPI映射——>交给asp.net(即为IIS的扩展)——>通知Application类创建一个应用 ...
随机推荐
- Spring集成ElasticSearch搜索引擎
目录 前期安装 Maven支持库安装 添加log4j的配置文件 创建Client客户端 实现增删改查以及符合查询 实现查询数据 实现添加数据 实现删除数据 实现修改数据 实现复合查询数据 Elasti ...
- (后端)安装mongodb以及设置为windows服务 详细步骤(转)
1.在data文件夹下新建一个log文件夹,用于存放日志文件,在log文件夹下新建文件mongodb.log 2.在 D:\mongodb文件夹下新建文件mongo.config,并用记事本打开mon ...
- php post接口,注册功能
功能描述:仅输入手机号和密码,实现注册功能.手机号有简单的验证,不可重复输入,否则会报500错误. 在使用 RestClient 进行post测试时,如果你把参数放在 [Headers]区块了,那么, ...
- VS错误:#error 指令: Please use the /MD switch for _AFXDLL builds
我在做MFC时遇到过这个问题,解决方法如下: 修改设置:工程(Project)-> 属性(Properties)-> 配置属性(Configuration Properties)-> ...
- flask based on tornado
from flask import Flask from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPSe ...
- MyBatis笔记----报错Exception in thread "main" org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.ij34.model.UserMapper.selectUser
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@41cf53f9: startup ...
- 安全之路 —— 利用APC队列实现跨进程注入
简介 在之前的文章中笔者曾经为大家介绍过使用CreateRemoteThread函数来实现远程线程注入(链接),毫无疑问最经典的注入方式,但也因为如此,这种方式到今天已经几乎被所有安全软件所防御.所以 ...
- Windows Server2008 R2安装wampserver缺少api-ms-win-crt-runtime-l1-1-0.dll解决方案
安装wampserver经常会遇到缺少各种dll文件的问题,可以在安装之前先安装一下微软运行库合集,但此时仍有可能缺少api-ms-win-crt-runtime-l1-1-0.dll文件,那么可以尝 ...
- Windows 版本说明,Enterprise、Ultimate、Home、Professional知多少
关于Windows 的安装光盘版本很多种,很多人不知道选择哪些. Ultimate 旗舰版,VISTA开始有了这个级别,是最全最高级的,一般程序开发的电脑,玩游戏的电脑,建议用它,不过对配置稍有一些要 ...
- java爬知乎问题的所有回答
突然想爬知乎问题的答案, 然后就开始研究知乎页面,刚开始是爬浏览器渲染好的页面, 解析DOM,找到特定的标签, 后来发现,每次只能得到页面加载出来的几条数据,想要更多就要下拉页面,然后浏览器自动加载几 ...