Asp.Net Core中服務(wù)的生命周期選項區(qū)別與用法詳解
前言
最近在做一個小的Demo中,在一個界面上兩次調(diào)用視圖組件,并且在視圖組件中都調(diào)用了數(shù)據(jù)庫查詢,結(jié)果發(fā)現(xiàn),一直報錯,將兩個視圖組件的調(diào)用分離,單獨進(jìn)行,卻又是正常的,尋找一番,發(fā)現(xiàn)是配置依賴注入服務(wù)時,對于服務(wù)的生命周期沒有配置得當(dāng)導(dǎo)致,特此做一次實驗來認(rèn)識三者之間(甚至是四者之間的用法及區(qū)別)。
本文demo地址(具體見WebApi控制器中):https://gitee.com/530521314/koInstance.git (本地下載)
一、服務(wù)的生命周期
在Asp.Net Core中,內(nèi)置容器負(fù)責(zé)管理服務(wù)的生命周期,從被依賴注入容器創(chuàng)建開始,等我們調(diào)用完服務(wù)時,到容器釋放該服務(wù)的所有實力為止,有幾種形式表現(xiàn):
1、Transient:每次請求服務(wù)時,都會創(chuàng)建一個新實例,這種生命周期適合用于輕量級服務(wù)(如Repository和ApplicationService服務(wù))。
2、Scoped:為每個HTTP請求創(chuàng)建一個實例,生命周期將橫貫整次請求。
3、SingleTon:在第一次請求服務(wù)時,為該服務(wù)創(chuàng)建一個實例,之后每次請求將會使用第一次創(chuàng)建好的服務(wù)。
4、Instance:與SingleTon類似,但在應(yīng)用程序啟動時會將該實例注冊到容器中,可以理解為比SingleTon還早存在。
應(yīng)用程序中相關(guān)服務(wù)的控制生命周期的方法時通過相應(yīng)的Add*指定,如下三種,當(dāng)然還可以通過擴(kuò)展方法來簡化ConfigurationServices方法中所見的代碼數(shù)量。
services.AddTransient<IApplicationService, ApplicationService>(); services.AddScoped<IApplicationService, ApplicationService>(); services.AddSingleton<IApplicationService, ApplicationService>();
二、代碼設(shè)計服務(wù)生命周期
首先設(shè)計一些服務(wù)相關(guān)的操作接口
public interface IOperation
{
Guid GetGuid();
}
public interface IOperationTransient: IOperation
{
}
public interface IOperationScoped : IOperation
{
}
public interface IOperationSingleton : IOperation
{
}
public interface IOperationInstance : IOperation
{
}
基礎(chǔ)服務(wù)接口
其次對這些操作類予以實現(xiàn)并生成相關(guān)服務(wù)
/// <summary>
/// 常規(guī)服務(wù)
/// </summary>
public class Operation : IOperation
{
private readonly Guid _guid;
public Operation()
{
_guid = Guid.NewGuid();
}
public Operation(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
/// <summary>
/// 瞬時服務(wù)
/// </summary>
public class OperationTransient : IOperationTransient
{
private readonly Guid _guid;
public OperationTransient()
{
_guid = Guid.NewGuid();
}
public OperationTransient(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
/// <summary>
/// 單次請求內(nèi)服務(wù)固定
/// </summary>
public class OperationScoped : IOperationScoped
{
private readonly Guid _guid;
public OperationScoped()
{
_guid = Guid.NewGuid();
}
public OperationScoped(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
/// <summary>
/// 所有請求內(nèi)固定服務(wù)
/// </summary>
public class OperationSingleton : IOperationSingleton
{
private readonly Guid _guid;
public OperationSingleton()
{
_guid = Guid.NewGuid();
}
public OperationSingleton(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
/// <summary>
/// 應(yīng)用程序內(nèi)固定服務(wù)
/// </summary>
public class OperationInstance : IOperationInstance
{
private readonly Guid _guid;
public OperationInstance()
{
_guid = Guid.NewGuid();
}
public OperationInstance(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
基礎(chǔ)服務(wù)具體實現(xiàn)
對基礎(chǔ)服務(wù)的聚合接口,提供統(tǒng)一服務(wù)接口
public interface IOperationService
{
/// <summary>
/// 獲取四種形式的Guid碼
/// </summary>
/// <returns></returns>
List<string> GetGuidString();
}
聚合服務(wù)接口
對基礎(chǔ)服務(wù)的聚合實現(xiàn),將基礎(chǔ)服務(wù)全部接入進(jìn)來作為統(tǒng)一服務(wù)
/// <summary>
/// 服務(wù)調(diào)用
/// </summary>
public class OperationService : IOperationService
{
public IOperationTransient _transientOperation { get; }
public IOperationScoped _scopedOperation { get; }
public IOperationSingleton _singletonOperation { get; }
public IOperationInstance _instanceOperation { get; }
public OperationService(IOperationTransient transientOperation,
IOperationScoped scopedOperation,
IOperationSingleton singletonOperation,
IOperationInstance instanceOperation)
{
_transientOperation = transientOperation;
_scopedOperation = scopedOperation;
_singletonOperation = singletonOperation;
_instanceOperation = instanceOperation;
}
public List<string> GetGuidString()
{
return new List<string>()
{
$"Transient:"+_transientOperation.GetGuid(),
$"Scoped:"+_scopedOperation.GetGuid(),
$"Singleton:" +_singletonOperation.GetGuid(),
$"Instance:"+_instanceOperation.GetGuid(),
};
}
}
聚合服務(wù)的實現(xiàn)
在控制器中進(jìn)行服務(wù)注入
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IOperationService _operationService;
public ValuesController(IOperationService operationService)
{
_operationService = operationService;
}
[HttpGet]
[Route(nameof(GetGuidString))]
public ActionResult<string> GetGuidString()
{
return string.Join("\n", _operationService.GetGuidString());
}
}
在StartUp中完成服務(wù)注入邏輯,這里實現(xiàn)服務(wù)注入的方式多種均可。
services.AddTransient<IOperationTransient, OperationTransient>(); services.AddScoped<IOperationScoped, OperationScoped>(); services.AddSingleton<IOperationSingleton, OperationSingleton>();//應(yīng)用程序啟動時便注入該實例 services.AddSingleton<IOperationInstance>(new OperationInstance(Guid.Empty)); services.AddTransient<IOperationService, OperationService>();
通過訪問預(yù)期Api地址可以得到不同的四種基礎(chǔ)服務(wù)的Guid信息,
第一次啟動程序(不關(guān)閉)發(fā)起訪問:
第二次(第一次基礎(chǔ)上再次訪問)發(fā)起訪問:
可以看見,兩次訪問下,Singleton和Instance是相同的,都是由應(yīng)用程序啟動時和應(yīng)用服務(wù)加載時決定完畢,Singleton在首次進(jìn)入服務(wù)時進(jìn)行分配,并始終保持不變,而Instance在應(yīng)用程序啟動時,便將實例注入,進(jìn)入服務(wù)也保持著最先的實例,沒有重新分配實例。而Transient和Scoped則進(jìn)行著變化。
關(guān)閉程序,重啟,第三次發(fā)起訪問:
可以見到,Singleton和Instance都發(fā)生了變化,也說明了之前在Singleton和Instance處寫上的作用。
接下來開始設(shè)計Transient和Scoped的不同之處,對于已有代碼加上新功能,此次我們只針對Scoped和Transient進(jìn)行比較。
首先在StartUp中將HttpContextAccessor服務(wù)注入,目的是在后期能夠針對Scoped獲取新的服務(wù)實例(盡管兩個實例是相同的)。
services.AddHttpContextAccessor();
接著在聚合服務(wù)中增加一個方法,用來針對Transient、Scoped測試。
/// <summary> /// 獲取Transient、Scoped的Guid碼 /// </summary> /// <returns></returns> List<string> GetTransientAndScopedGuidString();
在聚合服務(wù)實現(xiàn)中實現(xiàn)該方法并對已有的服務(wù)重新獲取實例,得到不同實例下的Guid碼。
public List<string> GetTransientAndScopedGuidString()
{
//var tempTransientService = (IOperationTransient)ServiceLocator.Instance.GetService(typeof(IOperationTransient));
var tempTransientService = (IOperationTransient)_httpContextAccessor.HttpContext.RequestServices.GetService(typeof(IOperationTransient));
var tempScopedService = (IOperationScoped)_httpContextAccessor.HttpContext.RequestServices.GetService(typeof(IOperationScoped));
return new List<string>()
{
$"原生Transient請求服務(wù):"+_transientOperation.GetGuid(),
$"手動Transient請求服務(wù):"+ tempTransientService.GetGuid(),
$"原生Scoped請求服務(wù):"+_scopedOperation.GetGuid(),
$"手動Scoped請求服務(wù):"+tempScopedService.GetGuid(),
};
}
在控制器部分調(diào)用該聚合服務(wù)即可,并返回相應(yīng)的結(jié)果,本次我返回的結(jié)果:
可以看到,對于Scoped來講,一次請求內(nèi)多次訪問同一個服務(wù)是共用一個服務(wù)實例的,而對于Transient則是,每次訪問都是新的服務(wù)實例。
至此,對于這四種服務(wù)生命周期算是掌握的差不多了。
參考:
蔣老師文章: http://chabaoo.cn/article/150103.htm
田園里的蟋蟀:http://chabaoo.cn/article/150102.htm
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
.net 運用二進(jìn)制位運算進(jìn)行數(shù)據(jù)庫權(quán)限管理
.net 運用二進(jìn)制位運算進(jìn)行數(shù)據(jù)庫權(quán)限管理 ,需要的朋友可以參考一下2013-02-02
.net+FusionChart實現(xiàn)動態(tài)顯示的柱狀圖和餅狀圖
這篇文章介紹了.net+FusionChart實現(xiàn)動態(tài)顯示柱狀圖和餅狀圖的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07
ASP.NET實現(xiàn)頁面?zhèn)髦档膸追N方法小結(jié)
這篇文章介紹了ASP.NET實現(xiàn)頁面?zhèn)髦档膸追N方法,有需要的朋友可以參考一下2013-11-11
創(chuàng)建一個ASP.NET MVC5項目的實現(xiàn)方法(圖文)
這篇文章主要介紹了創(chuàng)建一個ASP.NET MVC 5項目,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
ASP.NET MVC4中使用Html.DropDownListFor的方法示例
這篇文章主要介紹了ASP.NET MVC4中使用Html.DropDownListFor的方法,結(jié)合實例形式分析了控制器數(shù)據(jù)源及Html.DropDownListFor顯示操作的相關(guān)技巧,需要的朋友可以參考下2016-08-08
解決Visual Studio 2012 Update 4 RC啟動調(diào)試失敗的方案
這篇文章主要為大家詳細(xì)介紹了Visual Studio 2012 Update 4 RC啟動調(diào)試失敗的解決方案,感興趣的小伙伴們可以參考一下2016-05-05

