一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - ASP.NET Core 3框架揭秘之 異步線程無法使用IServiceProvider問題

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

2020-06-23 14:05Artech ASP.NET教程

這篇文章主要介紹了ASP.NET Core 3框架揭秘之異步線程無法使用IServiceProvider問題,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下

標題反映的是上周五一個同事咨詢我的問題,我覺得這是一個很好的問題。這個問題有助于我們深入理解依賴注入框架在ASP.NET Core中的應用,以及服務實例的生命周期。

 

一、問題重現

 

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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();");
}

我們創建了如下這個HomeController,它的構造函數中注入了一個IServiceProvider對象。在Action方法Index中,我們調用Task的靜態方法Run異步執行了一些操作。具體來說,在異步執行的操作中,我們利用調用上面注入的這個IServiceProvider對象的GetRequiredService<T>方法試圖獲取一個IFoobar服務實例。由于這段操作時在一個Try/Catch中執行的,拋出的異常消息的堆棧信息會直接輸出到控制臺上。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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();
 }
}

在運行該應用程序后,我們利用瀏覽器采用根路徑(“/”)對Action方法Index發起訪問后,服務端控制臺上會出現如下所示的錯誤信息。

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

 

二、ApplicationServices與RequestServices

 

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

一般來說,ApplicationServices用于提供管道構建過程中所需的服務實例,具體請求處理過程中所需的服務實例一般由RequestServices提供。具體來說,對于接收的每一個請求,ASP.NET Core框架都會利用ApplicationServices創建一個代表服務范圍的IServiceScope對象,后者就是對RequestServices的封裝。在完成了針對請求的處理之后,服務范圍被終結,RequestServices被Dispose。

對于我們演示的實例來說,注入到HomeController構造函數中的IServiceProvider是RequestServices,由于針對RequestServices的使用是在另一個后臺線程中執行的,并且在使用的時候針對當前請求的處理已經結束(因為我們人為等待了100毫秒),自然就會出現上圖所示的異常。

 

三、如何獲取ApplicationServices

 

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

?
1
2
3
4
5
6
7
public interface IHost : IDisposable
{
 Task StartAsync(CancellationToken cancellationToken = new CancellationToken());
 Task StopAsync(CancellationToken cancellationToken = new CancellationToken());
 
 IServiceProvider Services { get; }
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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();
 }
}

當我們采用如上的方式將RequestServices替換成ApplicationServices之后,我們的問題是否就解決了呢?在采用上面相同的方式進行測試之后,我們會發現服務端控制臺上出現了如下所示的錯誤消息。

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

 

四、服務實例的生命周期

 

上面的問題是由我們試圖利用一個代表“根容器”的IServiceProvider對象去解析一個生命周期模式為Scoped服務實例導致,具體的原因在《依賴注入[8]:服務實例的生命周期》已經講得很清楚了。為了解決這個問題,我們應該根據ApplicationServices創建一個“服務范圍”,并在該服務范圍內提取我們所需的服務實例。為了確保服務實例能夠被正常回收,我們還應該將代表服務范圍的IServiceScope對象及時終結掉。如下所示的是正確的編程方式。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
 }
}

 

五、統一的解決方案

 

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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();
 }
}

總結

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

原文鏈接:https://www.cnblogs.com/artech/p/async-di.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 耽美双性| 国产亚洲欧美成人久久片 | 女同志freelesvoices | 国产精品资源在线观看 | 国产精品www | 日本免费一区二区三区四区五六区 | 国产精品视频一区二区三区不卡 | 韩国帅男同gay网站 韩国三级在线播放 | 波多野结衣在线观看中文字幕 | 日本aaa大片 | 99久久精品免费看国产情侣 | 国产欧美日韩一区二区三区在线 | 百合互慰吃奶互揉漫画 | 欧美人与禽杂交大片 | 女八把屁股扒开让男生添 | 亚洲欧美国产精品久久久 | 日韩免费一级毛片 | 痴mu动漫成年动漫在线观看 | 万域之王动漫在线观看全集免费播放 | 水多多凹凸福利视频导航 | 国产精品四虎在线观看免费 | 免费视频专区一国产盗摄 | 国产视频自拍一区 | aigao视频| 日本中文字幕不卡在线一区二区 | 青草视频网 | 成人永久免费 | chinese帅男gay野外性 | 国产精品久久久久一区二区三区 | 久久久精品国产免费A片胖妇女 | 久久综合色超碰人人 | 糖心vlog视频永久破解版 | 美女班主任下面好爽好湿好紧 | 亚洲国产日韩欧美mv | 操岳母娘 | 日本免费高清在线观看播放 | 国产yw193.㎝m在线观看 | 国产乱子伦真实china | 国产自产2023最新麻豆 | 逼里逼里香 | 北岛玲亚洲一区在线观看 |