[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 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312820.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

经典排序算法(4)——折半插入排序算法详解

折半插入排序&#xff08;Binary Insertion Sort&#xff09;是一种插入排序算法&#xff0c;通过不断地将数据元素插入到合适的位置进行排序&#xff0c;在寻找插入点时采用了折半查找。 一、算法基本思想 &#xff08;1&#xff09;基本思想 折半插入排序的基本思想是&#x…

经典排序算法(5)——希尔排序算法详解

希尔排序&#xff08;Shell Sort&#xff09;是一种典型的插入排序算法&#xff0c;通过对原始序列进行分组进行排序。 一、算法基本思想 &#xff08;1&#xff09;基本思想 希尔排序是基于插入排序的以下两点性质而提出改进方法的&#xff1a; 插入排序在对几乎已经排好序的…

程序员修神之路--容器技术为什么会这么流行(记得去抽奖)

菜菜哥&#xff0c;你上次讲的kubernetes我研究了一下&#xff0c;你再给我讲讲docker呗docker可很流行呀kubernetes是容器编排技术&#xff0c;容器不就是指的docker吗&#xff1f;docker可不等于容器哦&#xff0c;docker只算是容器的一种吧&#xff0c;算了容器的典型代表容…

经典排序算法(6)——直接选择排序算法详解

直接选择排序&#xff08;Straight Select Sort&#xff09;是一种典型的选择排序算法&#xff0c;通过不断选择序列中最大&#xff08;小&#xff09;的元素。 一、算法基本思想 &#xff08;1&#xff09;基本思想 直接选择排序的基本思想就是&#xff1a;不断从未排序队列中…

一篇文章看懂Git是什么以及如何简单的上手Git

本文来自DotNET技术圈作者&#xff1a;显杰1.Git是什么Git是目前世界上最先进的分布式版本控制系统什么是版本控制系统&#xff1f;好比设计师从开始设计第一个版本的设计稿开始&#xff1a;Demo > Demo1 > Demo2 > ... >Demo1001 > Demo最终版本 > Demo最终…

[翻译] 使用 Serverless 和 .NET Core 构建飞速发展的架构

作者&#xff1a;Samuele RescaServerless 技术为开发人员提供了一种快速而独立的方式将实现投入生产。这种技术在企业的技术栈中日益流行&#xff0c;自 2017 年以来&#xff0c;它一直是 ThoughtWorks 技术雷达的实验级别的技术[译注&#xff1a;技术雷达是 ThoughtWorks 每半…

经典排序算法(7)——堆排序算法详解

堆排序&#xff08;Heap sort&#xff09;是指利用堆&#xff08;最大堆、最小堆&#xff09;这种数据结构所设计的一种排序算法。堆是一个完全二叉树的结构&#xff0c;并同时满足如下性质&#xff1a;即子结点的键值或索引总是小于&#xff08;或者大于&#xff09;它的父节点…

经典排序算法(8)——归并排序算法详解

归并排序&#xff08;Merge sort&#xff09;&#xff0c;是创建在归并操作上的一种有效的排序算法&#xff0c;效率为O(nlog n)。该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用&#xff0c;且各层分治递归可以同时进行。 一、算法基本思…

祝贺王远当选为中国区第二位 Teams MVP

今天一上班就传来喜讯&#xff0c;Microsoft Teams 大中华区技术社区专家委员会成员之一的王远成功当选了2020-2021年度微软最有价值专家&#xff08;MVP)&#xff0c;这是对他在基于Office 365的音视频会议系统&#xff08;尤其是在Microsoft Teams&#xff09;方面的深入研究…

经典排序算法(9)——桶排序算法详解

桶排序&#xff08;Bucket sort&#xff09;或所谓的箱排序&#xff0c;并不是比较排序&#xff0c;它不受到 O(nlogn) 下限的影响。 一、算法基本思想 &#xff08;1&#xff09;基本思想 桶排序工作的原理是将数组分到有限数量的桶子里&#xff0c;每个桶子再个别排序&#x…

[原]排错实战——使用process explorer替换任务管理器

前言 一般&#xff0c;我们会使用任务管理器查看系统中有哪些进程在运行&#xff0c;强制杀掉某个进程。可是系统自带的任务管理器功能有限&#xff0c;process explorer是一个功能更强大的工具。它可以让我们查看更多更详细的信息&#xff08; 比如查看某个进程的父进程&#…

ABP vNext中使用开源日志面板 LogDashboard

ABP vNext 使用 logdashboard本文示例源码&#xff1a;https://github.com/liangshiw/LogDashboard/tree/master/samples/abpvnextABPABP是aspnetcore3.0的开源web应用程序框架&#xff0c;非常适合现代web应用程序。有关ABP的更多内容可以查看官方文档Logdashboard可以直接在基…

经典排序算法(10)——基数排序算法详解

基数排序&#xff08;Radix sort&#xff09;是一种非比较型整数排序算法&#xff0c;其原理是将整数按位数切割成不同的数字&#xff0c;然后按每个位数分别比较。 一、算法基本思想 &#xff08;1&#xff09;基本思想 基数排序是基于桶排序来实现。通过键值的部分信息&#…

经典排序算法(11)——计数排序算法详解

计数排序&#xff08;Counting sort&#xff09;是一个非基于比较的排序算法&#xff0c;该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时&#xff0c;它的复杂度为Ο(nk)&#xff08;其中k是整数的范围&#xff09;&#xff0c;快于任何比较…

GitHub Actions,卧槽!牛批!

“ 阅读本文大概需要 19 分钟。 ”前段时间我更新了我的分布式爬虫管理框架—— Gerapy&#xff08;话都说到这儿了打个广告&#xff0c;跟繁琐的命令行说拜拜&#xff01;Gerapy分布式爬虫管理框架来袭&#xff01;&#xff0c;哇&#xff0c;哇&#xff0c;就是&#xff0c;哇…

平衡二叉树AVL详解

一、平衡二叉树的定义 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;又被称为AVL树&#xff0c;它且具有以下性质&#xff1a; &#xff08;1&#xff09;它是一棵空树或它的左右两个子树的高度差的绝对值不超过1&#xff1b; &#xff08;2&#xff09;并且左右两个…

【壹个小技巧】一看就会的CI/CD :Github Actions

什么是 CI/CD?我这里先不说概念&#xff0c;先说一个平时开发的场景问题&#xff1a;我们平时开发一个项目&#xff0c;经常会遇到这些“小”问题&#xff1a;就是如何保证自己的项目是正确的&#xff0c;至少拿给别人的时候&#xff0c;可以编译运行的&#xff1f;或者说多人…

Hyper-V虚拟机自动添加检查点和导出备份

背景说明笔者使用Hyper-V在内部搭建了大量的环境和系统&#xff0c;比如&#xff1a;k8s内部集群Azure Devops Server(TFS>VSTS>Azure Devops Server)SharePoint…大部分基本上都是用于内部研究、测试等场景&#xff0c;但是为了避免很多麻烦&#xff0c;必要的备份还是必…

哈夫曼树详解

一、哈夫曼树的定义 &#xff08;1&#xff09;简单路径长度 所谓树的简单路径长度&#xff0c;是指从树的跟节点到每个节点的路径长度之和。 完全二叉树是简单路径长度更小的二叉树。 &#xff08;2&#xff09;加权路径长度 所谓树的加权路径长度&#xff0c;是指树中所以带…

深入理解.NET Core的基元(三) - 深入理解runtimeconfig.json

原文&#xff1a;Deep-dive into .NET Core primitives, part 3: runtimeconfig.json in depth作者&#xff1a;Nate McMaster[1]译文&#xff1a;深入理解.NET Core 的基元&#xff08;三&#xff09; - 深入 runtimeconfig.json作者&#xff1a;Lamond Lu前情回顾深入理解.NE…