.Net Core3.0依赖注入DI
構(gòu)建ASP.NET Core應(yīng)用程序的時(shí)候,依賴注入已成為了.NET Core的核心,這篇文章,我們理一理依賴注入的使用方法。
不使用依賴注入
?首先,我們創(chuàng)建一個(gè)ASP.NET Core Mvc項(xiàng)目,定義個(gè)表達(dá)的愛(ài)服務(wù)接口,中國(guó)小伙類(lèi)實(shí)現(xiàn)這個(gè)類(lèi)如下:
public interface ISayLoveService { string SayLove(); } public class CNBoyService : ISayLoveService { public string SayLove() { return "安紅,我喜歡你"; } }在LoveController 控制器中調(diào)用 ISayLoveService的SayLove方法。
public class LoveController : Controller
{ private??ISayLoveService?loveService;????? public?IActionResult?Index() { loveService = new CNBoyService(); //中國(guó)小伙對(duì)安紅的表達(dá) ViewData["SayLove"] = loveService.SayLove(); return View(); } }輸出如圖:
小結(jié):LoveController控制器調(diào)用ISayLoveService服務(wù)的SayLove方法;我們的做法,直接在控制器去new CNBoyService()實(shí)例對(duì)象,也就是LoveController依賴ISayLoveService類(lèi)。
思考:能不能有種模式,new實(shí)例不要在使用的時(shí)候進(jìn)行創(chuàng)建,而是在外部或者有一個(gè)容器進(jìn)行管理;這不就是ioc思想嗎?好處,代碼的解耦、代碼更好的維護(hù)等等。
使用依賴注入
上面的疑惑,答案是肯定的,有!并且ASP.NET Core 支持依賴關(guān)系注入 (DI) 軟件設(shè)計(jì)模式(當(dāng)然也可以兼容第三方)。我們還使用上面的代碼,
服務(wù)注冊(cè)
?在Startup類(lèi)ConfigureServices方法中注冊(cè)服務(wù)容器中的依賴關(guān)系
? ? ? public void ConfigureServices(IServiceCollection services)
{ services.AddSingleton<ISayLoveService, CNBoyService>(); services.AddControllersWithViews(); }在LoveControlle控制器中,通過(guò)構(gòu)造函數(shù)注入
private readonly ISayLoveService loveService; public LoveController(ISayLoveService loveService) { this.loveService = loveService; } public IActionResult Index() { ViewData["SayLove"] = loveService.SayLove(); return View(); }LoveController 正在將ISayLoveService作為依賴項(xiàng)注入其構(gòu)造函數(shù)中,然后在Index方法中使用它。
推薦:
將注入的依賴項(xiàng)分配給只讀字段/屬性(以防止在方法內(nèi)部意外為其分配另一個(gè)值)。
使用接口或基類(lèi)抽象化依賴關(guān)系實(shí)現(xiàn)。
思考:服務(wù)注冊(cè)的時(shí)候使用的是 AddSingleton,如services.AddSingleton<ISayLoveService, CNBoyService>();還有其他的嗎?
服務(wù)生命周期
服務(wù)注冊(cè)的時(shí)候,ASP.NET Core支持指定三種生命周期如:
Singleton 單例
Scoped 范圍
Transient 短暫的
Singleton 僅創(chuàng)建一個(gè)實(shí)例。該實(shí)例在需要它的所有組件之間共享。因此始終使用同一實(shí)例。
Scoped 每個(gè)范圍創(chuàng)建一個(gè)實(shí)例。在對(duì)應(yīng)用程序的每個(gè)請(qǐng)求上都會(huì)創(chuàng)建一個(gè)范圍,因此每個(gè)請(qǐng)求將創(chuàng)建一次注冊(cè)為Scoped的任何組件。
Transient 在每次被請(qǐng)求時(shí)都會(huì)創(chuàng)建,并且永不共享。
為了能夠更好的裂解生命周期的概念,我們把上面代碼稍作改動(dòng),做一個(gè)測(cè)試:
ISayLoveService 新增個(gè)屬性LoveId,類(lèi)型為guid,
public interface ISayLoveService { Guid LoveId { get; } string SayLove(); } public?interface?ITransientSayLoveService?:?ISayLoveService { } public?interface?IScopedSayLoveService?:?ISayLoveService { } public?interface?ISingletonSayLoveService?:?ISayLoveService { } public interface ISingletonInstanceSayLoveService : ISayLoveService { }BoyService也很簡(jiǎn)單,在構(gòu)造函數(shù)中傳入一個(gè)Guid,并對(duì)它進(jìn)行賦值。
public class BoyService : ITransientSayLoveService, IScopedSayLoveService, ISingletonSayLoveService, ISingletonInstanceSayLoveService { public BoyService():this(Guid.NewGuid()) { } public?BoyService(Guid?id) { LoveId = id; } public?Guid?LoveId?{?get;?private?set;?} public?string?SayLove() { return LoveId.ToString(); } }?每個(gè)實(shí)現(xiàn)類(lèi)的構(gòu)造函數(shù)中,我們都產(chǎn)生了一個(gè)新的guid,通過(guò)這個(gè)GUID,我們可以判斷這個(gè)類(lèi)到底重新執(zhí)行過(guò)構(gòu)造函數(shù)沒(méi)有.
服務(wù)注冊(cè)代碼如下:
public void ConfigureServices(IServiceCollection services) { //生命周期設(shè)置為T(mén)ransient,因此每次都會(huì)創(chuàng)建一個(gè)新實(shí)例。 services.AddTransient<ITransientSayLoveService, BoyService>(); services.AddScoped<IScopedSayLoveService, BoyService>(); services.AddSingleton<ISingletonSayLoveService, BoyService>(); services.AddSingleton<ISingletonInstanceSayLoveService>(new BoyService(Guid.Empty)); services.AddControllersWithViews(); }在LifeIndex方法中多次調(diào)用ServiceProvider的GetService方法,獲取到的都是同一個(gè)實(shí)例。
public IActionResult LifeIndex() { ViewData["TransientSayLove1"] = HttpContext.RequestServices.GetService<ITransientSayLoveService>().SayLove(); ViewData["ScopedSayLove1"] = HttpContext.RequestServices.GetService<IScopedSayLoveService>().SayLove(); ViewData["SingletonSayLove1"] = HttpContext.RequestServices.GetService<ISingletonSayLoveService>().SayLove(); ViewData["SingletonInstanceSayLove1"] = HttpContext.RequestServices.GetService<ISingletonInstanceSayLoveService>().SayLove(); //同一個(gè)HTTP請(qǐng)求 ,在從容器中獲取一次 ViewData["TransientSayLove2"] = HttpContext.RequestServices.GetService<ITransientSayLoveService>().SayLove(); ViewData["ScopedSayLove2"] = HttpContext.RequestServices.GetService<IScopedSayLoveService>().SayLove(); ViewData["SingletonSayLove2"] = HttpContext.RequestServices.GetService<ISingletonSayLoveService>().SayLove(); ViewData["SingletonInstanceSayLove2"] = HttpContext.RequestServices.GetService<ISingletonInstanceSayLoveService>().SayLove(); return View(); }我們編寫(xiě)view頁(yè)面,來(lái)展示這些信息如下:
@{ ViewData["Title"] = "LifeIndex"; } <div class="row"> <div class="panel panel-default"> <div class="panel-heading"> <h2 class="panel-title">Operations</h2> </div> <div class="panel-body"> <h3>獲取第一次</h3> <dl> <dt>Transient1</dt> <dd>@ViewData["TransientSayLove1"] </dd> <dt>Scoped1</dt> <dd>@ViewData["ScopedSayLove1"]</dd> <dt>Singleton1</dt> <dd>@ViewData["SingletonSayLove1"] </dd> <dt>Instance1</dt> <dd>@ViewData["SingletonInstanceSayLove1"]</dd> </dl> <h3>獲取第二次</h3> <dl> <dt>Transient2</dt> <dd>@ViewData["TransientSayLove2"]</dd> <dt>Scoped2</dt> <dd>@ViewData["ScopedSayLove2"]</dd> <dt>Singleton2</dt> <dd>@ViewData["SingletonSayLove2"]</dd> <dt>Instance2</dt> <dd>@ViewData["SingletonInstanceSayLove2"]</dd> </dl> </div> </div> </div>運(yùn)行代碼第一次輸出:
我們發(fā)現(xiàn),在一次請(qǐng)求中,發(fā)現(xiàn)單例、范圍的生命周期的guid 沒(méi)有變化,說(shuō)明分別用的是同一個(gè)對(duì)象,而瞬態(tài)guid不同,說(shuō)明對(duì)象不是一個(gè)。
刷新之后,查看運(yùn)行效果
我們發(fā)現(xiàn)通過(guò)刷新之后,單例模式的guid還是跟首次看到的一樣,其他的都不同;
總結(jié):如果您將組件A注冊(cè)為單例,則它不能依賴已注冊(cè)“作用域”或“瞬態(tài)”生存期的組件。一般而言:組件不能依賴壽命短于其壽命的組件。如果默認(rèn)的DI容器不能滿足項(xiàng)目需求,可以替換成第三方的如功能強(qiáng)大的Autofac。
總結(jié)
以上是生活随笔為你收集整理的.Net Core3.0依赖注入DI的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C# 8 新特性 - using 声明
- 下一篇: .Net轻量状态机Stateless