07、NetCore2.0依賴注入(DI)之生命周期
NetCore2.0依賴注入框架(DI)是如何管理注入對象的生命周期的?生命周期有哪幾類,又是在哪些場景下應用的呢?
------------------------------------------------------------------------------------------------------------
寫在前面:這是一個系列的文章,總目錄請移步:NetCore2.0技術文章目錄
------------------------------------------------------------------------------------------------------------
一、生命周期的分類
來看看系統依賴注入框架(DI)的開源代碼:
namespace Microsoft.Extensions.DependencyInjection
{
public enum ServiceLifetime
{
Singleton,
Scoped,
Transient
}
}
從源碼可以看出,依賴注入框架(DI)支持三種生命周期管理模式
- Singleton
單例模式,服務在第一次請求時被創建,其后的每次請求都沿用這個已創建的服務。我們不用再自己寫單例了。
- Scoped
作用域模式,服務在每次請求時被創建,整個請求過程中都貫穿使用這個創建的服務。比如Web頁面的一次請求。
- Transient
瞬態模式,服務在每次請求時被創建,它最好被用於輕量級無狀態服務。
二、重現三種生命周期的應用場景
首先我們創建三個服務,用來提供GUID。
using System; namespace LifetimeOfDI { public interface IGuidService { Guid Id(); } public interface ITransientService : IGuidService { } public interface IScopedService : IGuidService { } public interface ISingletonService : IGuidService { } public class GuidServiceBase : IGuidService { private readonly Guid _item; public GuidServiceBase() { _item = Guid.NewGuid(); } public Guid Id() { return _item; } } public class TransientService : GuidServiceBase, ITransientService { } public class ScopedService : GuidServiceBase, IScopedService { } public class SingletonService : GuidServiceBase, ISingletonService { } }
然后用VS2017新建一個Mvc項目,在Startup類中注冊這三個服務
public void ConfigureServices(IServiceCollection services) { services.AddTransient<ITransientService, TransientService>(); services.AddScoped<IScopedService, ScopedService>(); services.AddSingleton<ISingletonService, SingletonService>(); services.AddMvc(); }
既然注冊了,在Controller中就可以使用這些服務了,我們采取構造函數注入的方式,來給Controller注入這些服務
using Microsoft.AspNetCore.Mvc; namespace LifetimeOfDI.Controllers { public class HomeController : Controller { private readonly ITransientService _guidTransientService; private readonly IScopedService _guidScopedService; private readonly ISingletonService _guidSingletonService; // 構造函數注入 public HomeController(ITransientService guidTransientService, IScopedService guidScopedService, ISingletonService guidSingletonService) { _guidTransientService = guidTransientService; _guidScopedService = guidScopedService; _guidSingletonService = guidSingletonService; } public IActionResult Index() { // 傳GUID給頁面 ViewBag.TransientItem = _guidTransientService.Id(); ViewBag.ScopedItem = _guidScopedService.Id(); ViewBag.SingletonItem = _guidSingletonService.Id(); return View(); } } }
在Index.cshtml頁面中顯示這三個GUID
@{ ViewData["Title"] = "Home Page"; } <div class="row"> <div> <h2>Guid Service Shows</h2> <h3>TransientGuid: @ViewBag.TransientItem</h3> <h3>ScopedGuid: @ViewBag.ScopedItem</h3> <h3>SingletonGuid: @ViewBag.SingletonItem</h3> </div> </div>
我們啟動兩個瀏覽器,可以看出單例模式的Guid在兩個瀏覽器中是一致的,而且,即使刷新瀏覽器,也不會改變;另外兩個生命周期的服務因為每次刷新都發起了一次新的請求,所以Guid都不同。

三、使用局部頁面技術驗證作用域生命周期的特點
上一節沒能驗證Scoped類型生命周期,因為每次刷新都發起了一次新的請求。我們需要驗證一個Web請求,對服務的多次使用。如何驗證呢?這里我們借助局部頁面技術。
新建一個局部頁面IndexPartial.cshtml,在局部頁面中引用我們的自定義服務命名空間,並注入三個服務,分別顯示其Id。
@*引用自定義接口的命名空間*@ @using LifetimeOfDI @*依賴注入*@ @inject ITransientService TransientService @inject IScopedService ScopedService @inject ISingletonService SingletonService @*輸出服務提供的Id*@ <div class="row"> <div> <h2>Guid Service Shows</h2> <h3>TransientGuid: @TransientService.Id()</h3> <h3>ScopedGuid: @ScopedService.Id()</h3> <h3>SingletonGuid: @SingletonService.Id()</h3> </div> </div>
在Index.cshtml頁面中兩次引用這個局部頁,這樣就可以展示,一次請求兩次調用服務接口的場景。
@{
ViewData["Title"] = "Home Page";
}
@Html.Partial("IndexPartial")
@Html.Partial("IndexPartial")
看看效果吧

從效果看,作用域生命周期內的Id在一次請求的多次調用中保持了一致性;而瞬態生命周期則每次調用都不同;單例生命周期則不用說了,不同請求的多次調用都不變,更不用說相同請求了。
至此,我們理解了三種生命周期的的特點,在業務開發中可以按需使用了。
