實戰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 類庫的時候,一定要理解清楚不同的生命周期的應用場景。
本文示例代碼:https://github.com/hstarorg/HstarDemoProject/tree/master/dotnet_demo/servicelifttime-demo/ServicelifttimeDemo
本文github地址
實戰Asp.Net Core:DI生命周期