title: 实战Asp.Net Core:DI生命周期
date: 2018-11-30 21:54:52
---html
Asp.Net Core
默认支持 DI(依赖注入)
软件设计模式,那使用 DI
的过程当中,咱们势必会接触到对象的生命周期,那么几种不一样的对象生命周期究竟是怎么样的呢?咱们拿代码说话。git
关于 DI 与 IOC:github
我的理解:IOC(控制反转)
是目的(下降代码、服务间的耦合),而 DI
是达到该目的的一种手段(具体办法)。c#
DI的生命周期,根据框架、库的不一样,会略有差别。此处,咱们就以微软的DI扩展为例,来讲下DI中经常使用的几种生命周期。设计模式
首先,咱们想象一个这样一个场景。假设咱们有寄快递的需求,那么咱们会致电快递公司:“咱们要寄快递,派一个快递员过来收货”。接着,快递公司会如何作呢?多线程
这对应到生命周期就是:框架
快递公司也就是咱们在DI中常说的容器(Container)了。性能
首先,咱们须要三个Services(Service1\Service2\Service3)内容一致,以下:this
// Service1.cs,Service二、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>(); }
对于单例来讲,咱们指望,在整个程序启动期间,都是同一个实例,因此,咱们只须要在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。// 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
生命周期,在做用域(能够简单理解为单次请求,实际上并不许确,注意,此处为考虑多线程的状况)内,都是使用的同一个实例。在不一样的请求之间,则是不一样的实例。
// 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,都是全新的实例。
Asp.Net Core中默认的DI,相对仍是比较简单的,只有三个生命周期。对于时下比较的依赖注入库,通常都会有更多的生命周期,有些还会有生命周期事件能够监控。
以 Autofac
为例,该类库提供了以下一些生命周期,能够作到更精细化的控制:
// 先建立做用域 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>(); } }
更多 Autofac 生命周期相关内容,请参考:https://autofac.readthedocs.io/en/latest/lifetime/instance-scope.html
本文主要简单演示了 Asp.Net Core 中默认的几种服务生命周期效果,也抛砖引玉的说了下 Autofac 中的服务生命周期。合理的利用生命周期,能够减小对象的建立,提交程序性能。可是,用错了生命周期,则容易产生隐含的bug。在使用 DI 类库的时候,必定要理解清楚不一样的生命周期的应用场景。