日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

[ASP.NET Core 3框架揭秘] 异步线程无法使用IServiceProvider?

發布時間:2023/12/4 asp.net 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [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接口。

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中執行的,拋出的異常消息的堆棧信息會直接輸出到控制臺上。

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發起訪問后,服務端控制臺上會出現如下所示的錯誤信息。

二、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。

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

IServiceProvider Services { get; }
}

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

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之后,我們的問題是否就解決了呢?在采用上面相同的方式進行測試之后,我們會發現服務端控制臺上出現了如下所示的錯誤消息。

四、服務實例的生命周期

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

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();
}
}

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


.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?

總結

以上是生活随笔為你收集整理的[ASP.NET Core 3框架揭秘] 异步线程无法使用IServiceProvider?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。