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 類庫的時候,必定要理解清楚不一樣的生命週期的應用場景。