欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ASP.NET Core 3框架揭秘之 異步線程無法使用IServiceProvider問題

 更新時(shí)間:2019年12月04日 11:34:28   作者:Artech  
這篇文章主要介紹了ASP.NET Core 3框架揭秘之異步線程無法使用IServiceProvider問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

標(biāo)題反映的是上周五一個(gè)同事咨詢我的問題,我覺得這是一個(gè)很好的問題。這個(gè)問題有助于我們深入理解依賴注入框架在ASP.NET Core中的應(yīng)用,以及服務(wù)實(shí)例的生命周期。

一、問題重現(xiàn)

我們通過一個(gè)簡單的實(shí)例來模擬該同事遇到的問題。我們采用極簡的方式創(chuàng)建了如下這個(gè)ASP.NET Core MVC應(yīng)用。如下面的代碼片段所示,除了注冊與ASP.NET Core MVC框架相關(guān)的服務(wù)與中間件之外,我們還調(diào)用了IHostBuilder的UseDefaultServiceProvider方法將配置選項(xiàng)ServiceProviderOptions的ValidateScopes屬性設(shè)置為True,以開啟針對(duì)服務(wù)范圍的驗(yàn)證。我們還采用Scoped生命周期模式注冊了服務(wù)IFoobar,具體的實(shí)現(xiàn)類型Foobar還實(shí)現(xiàn)了IDisposable接口。

public class Program
{
 public static void Main()
 {
 Host
 .CreateDefaultBuilder()
 .UseDefaultServiceProvider(options => options.ValidateScopes = true)
 .ConfigureWebHostDefaults(builder => builder
 .ConfigureLogging(logging => logging.ClearProviders())
 .ConfigureServices(services => services
  .AddScoped<IFoobar, Foobar>()
  .AddRouting()
  .AddControllers())
 .Configure(app => app
  .UseRouting()
  .UseEndpoints(endpoints => endpoints.MapControllers())))
 .Build()
 .Run();
 }
}

public interface IFoobar { }
public class Foobar : IFoobar, IDisposable
{
 public void Dispose() => Console.WriteLine("Foobar.Dispose();");
}

我們創(chuàng)建了如下這個(gè)HomeController,它的構(gòu)造函數(shù)中注入了一個(gè)IServiceProvider對(duì)象。在Action方法Index中,我們調(diào)用Task的靜態(tài)方法Run異步執(zhí)行了一些操作。具體來說,在異步執(zhí)行的操作中,我們利用調(diào)用上面注入的這個(gè)IServiceProvider對(duì)象的GetRequiredService<T>方法試圖獲取一個(gè)IFoobar服務(wù)實(shí)例。由于這段操作時(shí)在一個(gè)Try/Catch中執(zhí)行的,拋出的異常消息的堆棧信息會(huì)直接輸出到控制臺(tái)上。

public class HomeController: Controller
{
 private readonly IServiceProvider _requestServices;
 public HomeController(IServiceProvider requestServices)
 {
 _requestServices = requestServices;
 }
 [HttpGet("/")]
 public IActionResult Index()
 {
 Task.Run(async() => {
 try
 {
 await Task.Delay(100);
 var foobar = _requestServices.GetRequiredService<IFoobar>();
 }
 catch (Exception ex)
 {
 Console.WriteLine(ex.Message);
 Console.WriteLine(ex.StackTrace);
 }
 });
 return Ok();
 }
}

在運(yùn)行該應(yīng)用程序后,我們利用瀏覽器采用根路徑(“/”)對(duì)Action方法Index發(fā)起訪問后,服務(wù)端控制臺(tái)上會(huì)出現(xiàn)如下所示的錯(cuò)誤信息。

二、ApplicationServices與RequestServices

從上圖所示的錯(cuò)誤消息可以看出,問題出在我們試圖利用一個(gè)被Dispose的IServiceProvider來獲取我們所需的服務(wù)實(shí)例。我們知道,ASP.NET Core應(yīng)用在啟動(dòng)和請(qǐng)求處理過程中所需的服務(wù)幾乎都是由代表DI容器的IServiceProvider提供的。具體來說,這里存在著兩種類型的IServiceProvider對(duì)象,一種與當(dāng)前應(yīng)用的生命周期保持一致,我們一般將其稱為ApplicationServices,另一種則是具體針對(duì)每個(gè)請(qǐng)求的IServiceProvider對(duì)象,我們將其稱為RequestServices。

一般來說,ApplicationServices用于提供管道構(gòu)建過程中所需的服務(wù)實(shí)例,具體請(qǐng)求處理過程中所需的服務(wù)實(shí)例一般由RequestServices提供。具體來說,對(duì)于接收的每一個(gè)請(qǐng)求,ASP.NET Core框架都會(huì)利用ApplicationServices創(chuàng)建一個(gè)代表服務(wù)范圍的IServiceScope對(duì)象,后者就是對(duì)RequestServices的封裝。在完成了針對(duì)請(qǐng)求的處理之后,服務(wù)范圍被終結(jié),RequestServices被Dispose。

對(duì)于我們演示的實(shí)例來說,注入到HomeController構(gòu)造函數(shù)中的IServiceProvider是RequestServices,由于針對(duì)RequestServices的使用是在另一個(gè)后臺(tái)線程中執(zhí)行的,并且在使用的時(shí)候針對(duì)當(dāng)前請(qǐng)求的處理已經(jīng)結(jié)束(因?yàn)槲覀內(nèi)藶榈却?00毫秒),自然就會(huì)出現(xiàn)上圖所示的異常。

三、如何獲取ApplicationServices

既然與請(qǐng)求綁定的RequestServices不能用,我們只能使用與應(yīng)用綁定的ApplicationServices,那么后者如何得到呢?ASP.NET Core 3采用了基于IHost/IHostBuilder的承載方式,表示宿主的IHost接口具有如下所示的Services屬性,它返回的正式我們所需的ApplicationServices。

public interface IHost : IDisposable
{
 Task StartAsync(CancellationToken cancellationToken = new CancellationToken());
 Task StopAsync(CancellationToken cancellationToken = new CancellationToken());

 IServiceProvider Services { get; }
}

對(duì)于我們演示的程序來說,我們可以采用如下的方式在HomeController的構(gòu)造中注入IHost服務(wù)的方式間接地獲得這個(gè)ApplicationServices對(duì)象。

public class HomeController: Controller
{
 private readonly IServiceProvider _applicationServices;
 public HomeController(IHost host)
 {
 _applicationServices = host.Services;
 }
 [HttpGet("/")]
 public IActionResult Index()
 {
 Task.Run(async() => {
 try
 {
 await Task.Delay(100);
 var foobar = _applicationServices.GetRequiredService<IFoobar>();
 }
 catch (Exception ex)
 {
 Console.WriteLine(ex.Message);
 Console.WriteLine(ex.StackTrace);
 }
 });
 return Ok();
 }
}

當(dāng)我們采用如上的方式將RequestServices替換成ApplicationServices之后,我們的問題是否就解決了呢?在采用上面相同的方式進(jìn)行測試之后,我們會(huì)發(fā)現(xiàn)服務(wù)端控制臺(tái)上出現(xiàn)了如下所示的錯(cuò)誤消息。

四、服務(wù)實(shí)例的生命周期

上面的問題是由我們試圖利用一個(gè)代表“根容器”的IServiceProvider對(duì)象去解析一個(gè)生命周期模式為Scoped服務(wù)實(shí)例導(dǎo)致,具體的原因在《依賴注入[8]:服務(wù)實(shí)例的生命周期》已經(jīng)講得很清楚了。為了解決這個(gè)問題,我們應(yīng)該根據(jù)ApplicationServices創(chuàng)建一個(gè)“服務(wù)范圍”,并在該服務(wù)范圍內(nèi)提取我們所需的服務(wù)實(shí)例。為了確保服務(wù)實(shí)例能夠被正?;厥?,我們還應(yīng)該將代表服務(wù)范圍的IServiceScope對(duì)象及時(shí)終結(jié)掉。如下所示的是正確的編程方式。

public class HomeController: Controller
{
 private readonly IServiceProvider _applicationServices;
 public HomeController(IHost host)
 {
 _applicationServices = host.Services;
 }
 [HttpGet("/")]
 public IActionResult Index()
 {
 Task.Run(async() => {
 await Task.Delay(100);
 using (var scope = _applicationServices.CreateScope())
 {
 var foobar = scope.ServiceProvider.GetRequiredService<IFoobar>();
 }
 });
 return Ok();
 }
}

五、統(tǒng)一的解決方案

之前我們將問題的解決方案落實(shí)在如何獲取與當(dāng)前應(yīng)用具有相同生命周期的ApplicationServices上,所以我們采用注入IHost的方式得到這個(gè)ApplicationServices。如果采用傳統(tǒng)的基于IWebHost/IWebHostBuilder的承載方式,IHost自然是獲取不到了。但是我們是真的需要這個(gè)ApplicationServices對(duì)象嗎?其實(shí)不是,我們真正需要的是利用它創(chuàng)建一個(gè)代表服務(wù)范圍的IServiceScope對(duì)象,并在該范圍內(nèi)消費(fèi)我們所需的服務(wù)實(shí)例。由于IServiceScope是通過IServiceScopeFactory創(chuàng)建的,所以我們只需要注入IServiceScopeFactory即可。

public class HomeController : Controller
{
 private readonly IServiceScopeFactory _serviceScopeFactory;

 public HomeController(IServiceScopeFactory serviceScopeFactory)
 {
 _serviceScopeFactory = serviceScopeFactory;
 }

 [HttpGet("/")]
 public IActionResult Index()
 {
 Task.Run(async () =>
 {
 await Task.Delay(100);
 using (var scope = _serviceScopeFactory.CreateScope())
 { 
 var foobar = scope.ServiceProvider.GetRequiredService<IFoobar>();
 }
 });
 return Ok();
 }
}

總結(jié)

以上所述是小編給大家介紹的ASP.NET Core 3框架揭秘之 異步線程無法使用IServiceProvider問題,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!

相關(guān)文章

  • 淺談ADO.NET數(shù)據(jù)庫腳本

    淺談ADO.NET數(shù)據(jù)庫腳本

    最近跟著公司做了一個(gè)小項(xiàng)目,雖然不是很好,卻也有點(diǎn)心得,在此和大家分享一下,希望高手能不吝賜教啊。。。還有很多得更加努力學(xué)習(xí)啊
    2015-06-06
  • webapi中如何使用依賴注入

    webapi中如何使用依賴注入

    本篇將要和大家分享的是webapi中如何使用依賴注入,依賴注入這個(gè)東西在接口中常用,實(shí)際工作中也用的比較頻繁,因此這里分享兩種在api中依賴注入的方式Ninject和Unity。下面跟著小編一起來看下吧
    2017-02-02
  • VS2005 水晶報(bào)表在時(shí)部署時(shí)遇到的問題

    VS2005 水晶報(bào)表在時(shí)部署時(shí)遇到的問題

    前幾天在服務(wù)器上部署一個(gè)B/S程序的時(shí)候,程序中的水晶報(bào)表部分出了些問題,報(bào)錯(cuò):Server Error in '/' Application.
    2010-02-02
  • asp.net 數(shù)據(jù)綁定的實(shí)例代碼

    asp.net 數(shù)據(jù)綁定的實(shí)例代碼

    這篇文章介紹了asp.net 數(shù)據(jù)綁定的實(shí)例代碼,有需要的朋友可以參考一下
    2013-07-07
  • .NET微信開發(fā)之PC 端微信掃碼注冊和登錄功能實(shí)現(xiàn)

    .NET微信開發(fā)之PC 端微信掃碼注冊和登錄功能實(shí)現(xiàn)

    這篇文章主要介紹了.NET微信開發(fā)之PC 端微信掃碼注冊和登錄功能實(shí)現(xiàn)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-09-09
  • OpenCV 3.1.0+VS2015開發(fā)環(huán)境配置教程

    OpenCV 3.1.0+VS2015開發(fā)環(huán)境配置教程

    這篇文章主要為大家詳細(xì)介紹了OpenCV 3.1.0+VS2015開發(fā)環(huán)境配置教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • .net core Api 部署到Linux的方法步驟

    .net core Api 部署到Linux的方法步驟

    這篇文章主要介紹了.net core Api 部署到Linux的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • AspNetCore中的中間件詳解

    AspNetCore中的中間件詳解

    ASP.NET Core處理請(qǐng)求的方式看做是一個(gè)管道,中間件是組裝到應(yīng)用程序管道中用來處理請(qǐng)求和響應(yīng)的組件,這篇文章主要介紹了AspNetCore中的中間件詳解,需要的朋友可以參考下
    2023-05-05
  • ASP.NET Ref和Out關(guān)鍵字區(qū)別分析

    ASP.NET Ref和Out關(guān)鍵字區(qū)別分析

    類型介紹 在幾乎所有的OOP語言中,都存在2種類型的值。
    2009-02-02
  • 如何ASP.NET Core Razor中處理Ajax請(qǐng)求

    如何ASP.NET Core Razor中處理Ajax請(qǐng)求

    本篇技術(shù)文章主要給大家講述了如何ASP.NET Core Razor中處理Ajax請(qǐng)求這方面的知識(shí)點(diǎn),有興趣的朋友參考下。
    2018-01-01

最新評(píng)論