当前位置: 首页 > news >正文

丽江旅游网站建设wordpress畅言怎么样

丽江旅游网站建设,wordpress畅言怎么样,游戏网站域名,美业设计网站前言问题的起因是在帮同事解决遇到的一个问题#xff0c;他的本意是在EF Core中为了解决避免多个线程使用同一个DbContext实例的问题。但是由于对Microsoft.Extensions.DependencyInjection体系的深度不是很了解#xff0c;结果遇到了新的问题#xff0c;当时整得我也有点蒙… 前言    问题的起因是在帮同事解决遇到的一个问题他的本意是在EF Core中为了解决避免多个线程使用同一个DbContext实例的问题。但是由于对Microsoft.Extensions.DependencyInjection体系的深度不是很了解结果遇到了新的问题当时整得我也有点蒙了所以当时也没解决而且当时快下班了就想着第二天再解决。在地铁上经过我一系列的思维跳跃终于想到了问题的原因第二天也顺利的解决了这个问题。虽然我前面说了EFCore但是本质和EFCore没有关系只是凑巧。解决了之后觉得这个问题是个易错题觉得挺有意思的便趁机记录一下。问题演示接下来我们还原一下当时的场景以下代码只是作为演示无任何具体含义只是为了让操作显得更清晰一下接下来就贴一下当时的场景代码[Route(api/[controller]/[action])] [ApiController] public class InformationController : ControllerBase {private readonly LibraryContext _libraryContext;private readonly IServiceProvider _serviceProvider;private readonly ILoggerInformationController _logger;public InformationController(LibraryContext libraryContext, IServiceProvider serviceProvider,ILoggerInformationController logger){_libraryContext  libraryContext;_serviceProvider  serviceProvider;_logger  logger;}[HttpGet]public string GetFirst(){var caseInfo  _libraryContext.Informations.Where(i  i.IsDelete  0).FirstOrDefault();//这里直接使用了Task方式Task.Run(()  {try{//Task里创建了新的IServiceScopeusing var scope  _serviceProvider.CreateScope();//通过IServiceScope创建具体实例LibraryContext dbContext  scope.ServiceProvider.GetServiceLibraryContext();var list  dbContext!.Informations.Where(i  i.IsDelete  0).Take(100).ToList();}catch (Exception ex){_logger.LogError(ex.Message, ex);}});return caseInfo.Title;} }再次强调一下上述代码纯粹是为了让演示更清晰无任何业务含义不喜勿喷。咱们首先看一下这段代码表现出来的意思就是在ASP.NET Core的项目里在Task.Run里使用IServiceProvider去创建Scope的场景。如果对ASP.NET Core Controller生命周期和IServiceProvider不够了解的话会很容易遇到这个问题且不知道是什么原因。上述这段代码会偶现一个错误Cannot access a disposed object. Object name: IServiceProvider.这里为什么说是偶现呢因为会不会出现异常完全取决于Task.Run里的代码是在当前请求输出之前执行完成还是之后完成。说到这里相信有一部分同学已经猜到了代码报错的原因了。问题的本质很简单是因为IServiceProvider被释放掉了。我们知道默认情况下ASP.NET Core为每次请求处理会创建单独的IServiceScope这会关乎到声明周期为Scope对象的声明周期。所以如果Task.Run里的逻辑在请求输出之前执行完成那么代码运行没任何问题。如果是在请求完成之后完成再执行CreateScope操作那必然会报错。因为Task.Run里的逻辑何时被执行这个是由系统CPU调度本身决定的特别是CPU比较繁忙的时候这种异常会变得更加频繁。这个问题不仅仅是在Task.Run这种场景里类似的本质就是在一个IServiceScope里创建一个新的子Scope作用域的时候这个时候需要注意的是父级的IServiceProvider释放问题,如果父级的IServiceProvider已经被释放了那么基于这个Provider再去创建Scope则会出现异常。但是这个问题在结合Task或者多线程的时候更容易出现问题。解决问题既然我们知道了它为何会出现异常那么解决起来也就顺理成章了。那就是保证当前请求执行完成之前最好保证Task.Run里的逻辑也要执行完成所以我们上述的代码会变成这样[HttpGet] public async Taskstring GetFirst() {var caseInfo  _libraryContext.Informations.Where(i  i.IsDelete  0).FirstOrDefault();//这里使用了await Task方式await Task.Run(()  {try{//Task里创建了新的IServiceScopeusing var scope  _serviceProvider.CreateScope();//通过IServiceScope创建具体实例LibraryContext dbContext  scope.ServiceProvider.GetServiceLibraryContext();var list  dbContext!.Informations.Where(i  i.IsDelete  0).Take(100).ToList();}catch (Exception ex){_logger.LogError(ex.Message, ex);}});return caseInfo.Title; }试一下发现确实能解决问题因为等待Task完成能保证Task里的逻辑能在请求执行完成之前完成。但是很多时候我们并不需要等待Task执行完成因为我们就是希望它在后台线程去执行这些操作而不需要阻塞执行。上面我们提到了本质是解决在IServiceScope创建子Scope时遇到的问题因为这里注入进来的IServiceProvider本身是Scope的只在当前请求内有效所以基于IServiceProvider去创建IServiceScope要考虑到当前IServiceProvider是否释放。那么我们就得打破这个枷锁我们要想办法在根容器中去创建新的IServiceScope。这一点我大微软自然是考虑到了在Microsoft.Extensions.DependencyInjection体系中提供了IServiceScopeFactory这个根容器的作用域基于根容器创建的IServiceScope可以得到平行与当前请求作用域的独立的作用域而不受当前请求的影响。改造上面的代码用以下形式[Route(api/[controller]/[action])] [ApiController] public class InformationController : ControllerBase {private readonly LibraryContext _libraryContext;private readonly IServiceScopeFactory _scopeFactory;private readonly ILoggerInformationController _logger;public InformationController(LibraryContext libraryContext, IServiceScopeFactory scopeFactory,ILoggerInformationController logger){_libraryContext  libraryContext;_scopeFactory  scopeFactory;_logger  logger;}[HttpGet]public string GetFirst(){var caseInfo  _libraryContext.Informations.Where(i  i.IsDelete  0).FirstOrDefault();//这里直接使用了Task方式Task.Run(()  {try{//Task里创建了新的IServiceScopeusing var scope  _scopeFactory.CreateScope();//通过IServiceScope创建具体实例LibraryContext dbContext  scope.ServiceProvider.GetServiceLibraryContext();var list  dbContext!.Informations.Where(i  i.IsDelete  0).Take(100).ToList();}catch (Exception ex){_logger.LogError(ex.Message, ex);}});return caseInfo.Title;} }如果你是调试起来的话你可以看到IServiceScopeFactory的具体实例是Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope类型的它里面包含了一个IsRootScope属性通过这个属性我们可以知道当前容器作用域是否是根容器作用域。当使用IServiceProvider实例的时候IsRootScope为false当使用IServiceScopeFactory实例的时候IsRootScope为true。使用CreateScope创建IServiceScope实例的时候注意用完了需要释放否则可能会导致Transient和Scope类型的实例得不到释放。在之前的文章咱们曾提到过Transient和Scope类型的实例都是在当前容器作用域释放的时候释放的这个需要注意一下。问题探究上面我们了解到了在每次请求的时候使用IServiceProvider和使用IServiceScopeFactory的时候他们作用域的实例来源是不一样的。IServiceScopeFactory来自根容器IServiceProvider则是来自当前请求的Scope。顺着这个思路我们可以看一下他们两个究竟是如何的不相同。这个问题还得从构建Controller实例的时候注入到Controller中的实例作用域的问题。请求中的IServiceProvider在之前的文章ASP.NET Core Controller与IOC的羁绊[1]我们知道Controller是每次请求都会创建新的实例我们再次拿出来这段核心的代码来看一下在DefaultControllerActivator类的Create方法中[点击查看源码[2]]internal class DefaultControllerActivator : IControllerActivator {private readonly ITypeActivatorCache _typeActivatorCache;public DefaultControllerActivator(ITypeActivatorCache typeActivatorCache){_typeActivatorCache  typeActivatorCache;}public object Create(ControllerContext controllerContext){//省略一系列判断代码var serviceProvider  controllerContext.HttpContext.RequestServices;//这里传递的IServiceProvider本质就是来自HttpContext.RequestServicesreturn _typeActivatorCache.CreateInstanceobject(serviceProvider, controllerTypeInfo.AsType());} }通过这个方法我们可以看到创建Controller实例时如果存在构造依赖本质则是通过HttpContext.RequestServices实例创建出来的而它本身就是IServiceProvider的实例ITypeActivatorCache实例中则存在真正创建Controller实例的逻辑具体可以查看TypeActivatorCache类的实现[点击查看源码[3]]internal class TypeActivatorCache : ITypeActivatorCache {private readonly FuncType, ObjectFactory _createFactory (type)  ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);private readonly ConcurrentDictionaryType, ObjectFactory _typeActivatorCache new ConcurrentDictionaryType, ObjectFactory();public TInstance CreateInstanceTInstance(IServiceProvider serviceProvider,Type implementationType){//省略一系列判断代码var createFactory  _typeActivatorCache.GetOrAdd(implementationType, _createFactory);//创建Controller的时候需要的依赖实例都是来自IServiceProviderreturn (TInstance)createFactory(serviceProvider, arguments: null);} }其实在这里我们就可以得到一个结论我们在当前请求默认通过构造注入的IServiceProvider的实例其实就是HttpContext.RequestServices,也就是针对当前请求的作用域有效同样的是来自当前作用域的Scope周期的对象实例也是在当前请求结束就会释放。验证这个很简单可以写个demo来演示一下[Route(api/[controller]/[action])] [ApiController] public class InformationController : ControllerBase {private readonly IServiceProvider _serviceProvider;public InformationController(IServiceProvider serviceProvider){_serviceProvider  serviceProvider;}[HttpGet]public bool[] JudgeScope([FromServices]IServiceProvider scopeProvider){//比较构造注入的和在HttpContext获取的bool isEqualOne  _serviceProvider  HttpContext.RequestServices;//比较通过Action绑定的和在HttpContext获取的bool isEqualTwo  scopeProvider  HttpContext.RequestServices;return new[] { isEqualOne, isEqualTwo };} }毫无疑问默认情况下isEqualOne和isEqualTwo的结构都是true这也验证了我们上面的结论。因此在当前请求默认注入IServiceProvider实例的时候都是来自HttpContext.RequestServices的实例。请求中的IServiceProvider和IServiceScopeFactory上面我们看到了在当前请求中获取IServiceProvider实例本身就是Scope的而且在当前请求中通过各种注入方式获取到的实例都是相同的。那么接下来我们就可以继续跟踪本质的HttpContext.RequestServices的IServiceProvider到底来自什么地方呢我们找到HttpContext默认的实现类DefaultHttpContext中关于RequestServices属性的定义[点击查看源码[4]]//接受 public IServiceScopeFactory ServiceScopeFactory { get; set; }  default!; //数据来自RequestServicesFeature private static readonly FuncDefaultHttpContext, IServiceProvidersFeature _newServiceProvidersFeature  context  new RequestServicesFeature(context, context.ServiceScopeFactory); //缓存来自_newServiceProvidersFeature private IServiceProvidersFeature ServiceProvidersFeature _features.Fetch(ref _features.Cache.ServiceProviders, this, _newServiceProvidersFeature)!; //数据来自ServiceProvidersFeature的RequestServices public override IServiceProvider RequestServices {get { return ServiceProvidersFeature.RequestServices; }set { ServiceProvidersFeature.RequestServices  value; } }通过上面的源码我们可以看到HttpContext.RequestServices的数据最终来自RequestServicesFeature类的RequestServices属性我们可以直接找到RequestServicesFeature类的定义[点击查看源码[5]]public class RequestServicesFeature : IServiceProvidersFeature, IDisposable, IAsyncDisposable {private readonly IServiceScopeFactory? _scopeFactory;private IServiceProvider? _requestServices;private IServiceScope? _scope;private bool _requestServicesSet;private readonly HttpContext _context;public RequestServicesFeature(HttpContext context, IServiceScopeFactory? scopeFactory){_context  context;_scopeFactory  scopeFactory;}public IServiceProvider RequestServices{get{if (!_requestServicesSet  _scopeFactory ! null){//释放掉之前没释放掉的RequestServicesFeature实例_context.Response.RegisterForDisposeAsync(this);//通过IServiceScopeFactory创建Scope_scope  _scopeFactory.CreateScope();//RequestServices来自IServiceScopeFactory的CreateScope实例_requestServices  _scope.ServiceProvider;//填充已经设置了RequestServices的标识_requestServicesSet  true;}return _requestServices!;}set{_requestServices  value;_requestServicesSet  true;}}//释放的真实逻辑public ValueTask DisposeAsync(){switch (_scope){case IAsyncDisposable asyncDisposable:var vt  asyncDisposable.DisposeAsync();if (!vt.IsCompletedSuccessfully){return Awaited(this, vt);}vt.GetAwaiter().GetResult();break;case IDisposable disposable:disposable.Dispose();break;}//释放时重置相关属性_scope  null;_requestServices  null;return default;static async ValueTask Awaited(RequestServicesFeature servicesFeature, ValueTask vt){await vt;servicesFeature._scope  null;servicesFeature._requestServices  null;}}//IDisposable的Dispose的方法通过using可隐式调用public void Dispose(){DisposeAsync().AsTask().GetAwaiter().GetResult();} }通过上面的两段源码我们得到了许多关于IServiceProvider和IServiceScopeFactory的相关信息。• DefaultHttpContext的RequestServices值来自于RequestServicesFeature实例的RequestServices属性• RequestServicesFeature的RequestServices属性的值通过IServiceScopeFactory通过CreateScope创建的• 构建RequestServicesFeature的IServiceScopeFactory值来自于DefaultHttpContext的ServiceScopeFactory属性那么接下来我们直接可以找到DefaultHttpContext的ServiceScopeFactory属性是谁给它赋的值我们找到创建HttpContext的地方在DefaultHttpContextFactory的Create方法里[点击查看源码[6]]public class DefaultHttpContextFactory : IHttpContextFactory {private readonly IHttpContextAccessor? _httpContextAccessor;private readonly FormOptions _formOptions;private readonly IServiceScopeFactory _serviceScopeFactory;public DefaultHttpContextFactory(IServiceProvider serviceProvider){_httpContextAccessor  serviceProvider.GetServiceIHttpContextAccessor();_formOptions  serviceProvider.GetRequiredServiceIOptionsFormOptions().Value;//通过IServiceProvider的GetRequiredService直接获取IServiceScopeFactory_serviceScopeFactory  serviceProvider.GetRequiredServiceIServiceScopeFactory();}//创建HttpContext实例的方法public HttpContext Create(IFeatureCollection featureCollection){if (featureCollection is null){throw new ArgumentNullException(nameof(featureCollection));}var httpContext  new DefaultHttpContext(featureCollection);Initialize(httpContext);return httpContext;}private DefaultHttpContext Initialize(DefaultHttpContext httpContext){//IHttpContextAccessor也是在这里赋的值if (_httpContextAccessor ! null){_httpContextAccessor.HttpContext  httpContext;}httpContext.FormOptions  _formOptions;//DefaultHttpContext的ServiceScopeFactory属性值来自注入的IServiceProviderhttpContext.ServiceScopeFactory  _serviceScopeFactory;return httpContext;} }这里我们可以看到IServiceScopeFactory的实例来自于通过DefaultHttpContextFactory注入的IServiceProvider实例这里获取IServiceScopeFactory的地方并没有CreateScope所以这里的IServiceScopeFactory和IServiceProvider中的实例都是来自根容器。这个我们还可以通过注册DefaultHttpContextFactory地方看到 [点击查看源码[7]]services.TryAddSingletonIHttpContextFactory, DefaultHttpContextFactory();通过这里可以看到DefaultHttpContextFactory注册的是单例模式注册它的地方则是在IHostBuilder的ConfigureServices方法里。关于每次请求的创建流程不是本文的重点但是为了让大家对本文讲解的IServiceScopeFactory和IServiceProvider来源更清楚咱们可以大致的描述一下• GenericWebHostService类实现自IHostedService在StartAsync方法中启动了IServer实例默认则是启动的Kestrel。• IServer启动的方法StartAsync中会传递HostingApplication实例构建HostingApplication实例的时候则会依赖IHttpContextFactory实例而IHttpContextFactory实例则是在构建GenericWebHostService服务的时候注入进来的。• 当每次请求ASP.NET Core服务的时候会调用HostingApplication的CreateContext方法该方法中则会创建HttpContext实例每次请求结束后则调用该类的DisposeContext释放HttpContext实例。说了这么多其实就是为了方便让大家得到一个关系即在每次请求中获取的IServiceProvider实例来自HttpContext.RequestServices实例HttpContext.RequestServices实例来自IServiceScopeFactory来自CreateScope方法创建的实例而IServiceScopeFactory实例则是来自根容器且DefaultHttpContextFactory的生命周期则和当前ASP.NET Core保持一致。后续插曲就在解决这个问题后不久有一次不经意间翻阅微软的官方文档发现官方文档有提到相关的问题而且也是结合efcore来讲的。标题是《Do not capture services injected into the controllers on background threads》[8]翻译成中文大概就是不要在后台线程上捕获注入控制器的服务说的正是这个问题微软给我们的建议是• 注入一个IServiceScopeFactory以便在后台工作项中创建一个范围。• IServiceScopeFactory是一个单例对象。• 在后台线程中创建一个新的依赖注入范围。• 不引用控制器中的任何东西。• 不从传入请求中捕获DbContext。得到的结论和我们在本文描述的基本上是差不多的而且微软也很贴心的给我们提供了相关示例[HttpGet(/fire-and-forget-3)] public IActionResult FireAndForget3([FromServices]IServiceScopeFactory serviceScopeFactory) {_  Task.Run(async () {await Task.Delay(1000);using (var scope  serviceScopeFactory.CreateScope()){var context  scope.ServiceProvider.GetRequiredServiceContosoDbContext();context.Contoso.Add(new Contoso());await context.SaveChangesAsync();                                        }});return Accepted(); }原来还是自己的坑自己最了解也不得不说微软现在的文档确实挺详细的同时也提醒我们有空还是得多翻一翻文档避免踩坑。总结    本文主要是通过帮助同事解决问题而得到的灵感觉得挺有意思的希望能帮助更多的人了解这个问题且能避免这个问题。我们应该深刻理解ASP.NET Core处理每次请求则都会创建一个Scope这会影响当前请求获取的IServiceProvider实例和通过IServiceProvider创建的生命周期为Scope的实例。如果把握不住则可以理解为当前请求直接注入的服务和当前服务直接注入的IServiceProvider实例。如果想获取根容器的实例则可以通过获取IServiceScopeFactory实例获取最后请注意IServiceScope的释放问题。     曾几何时特别喜欢去解决遇到的问题特别喜欢那种解决问题沉浸其中的过程。解决了问题了解到为什么会让自己感觉很通透也更深刻不经意间的也扩展了自己的认知边界。这个过程得到的经验是一种通识是一种意识。而思维和意识则是我们适应这个不断在变化时代的底层逻辑。引用链接[1] ASP.NET Core Controller与IOC的羁绊: https://www.cnblogs.com/wucy/p/14222973.html[2] 点击查看源码: https://github.com/dotnet/aspnetcore/blob/v6.0.7/src/Mvc/Mvc.Core/src/Controllers/DefaultControllerActivator.cs#L56[3] 点击查看源码: https://github.com/dotnet/aspnetcore/blob/v6.0.7/src/Mvc/Mvc.Core/src/Infrastructure/TypeActivatorCache.cs#L38[4] 点击查看源码: https://github.com/dotnet/aspnetcore/blob/v6.0.7/src/Http/Http/src/DefaultHttpContext.cs#L176[5] 点击查看源码: https://github.com/dotnet/aspnetcore/blob/v6.0.7/src/Http/Http/src/Features/RequestServicesFeature.cs#L33[6] 点击查看源码: https://github.com/dotnet/aspnetcore/blob/v6.0.7/src/Hosting/Hosting/src/Http/DefaultHttpContextFactory.cs#L45[7] 点击查看源码: https://github.com/dotnet/aspnetcore/blob/v6.0.7/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs#L93[8] 《Do not capture services injected into the controllers on background threads》: https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?viewaspnetcore-6.0#do-not-capture-services-injected-into-the-controllers-on-background-threads
http://www.huolong8.cn/news/367271/

相关文章:

  • 三水做网站图案logo设计
  • 北京比较好的网站开发公司成都网站建设中心
  • 外贸专业网站建设莱芜雪野湖地图
  • 十大行情软件网站下载重庆网站设计公司价格
  • 重庆价格低建设网站公司wordpress 官方文档
  • 网站整体风格wordpress建站两秒打开
  • 建行企业网站刚开始做汽配网站要进货
  • 高校财务网站建设营销型网站建设公司哪里有
  • 建商城网站需要什么条件重庆做网站的公司有哪些
  • 以遇见为主题做网站需要哪些技术
  • 手机网站建设制作教程视频深圳龙华新区网站建设
  • 国外电商网站建设北京网站改版要注意什么
  • 余江区建设局网站百度贴吧网页入口
  • 质控中心网站建设申请北京小程序开发平台
  • 制作网页和做网站是一个意思吗网站制作三站
  • 高端网站制作西安到北京飞机
  • 网站1g的空间能用多久餐厅网页设计素材
  • 珠海网站建设zhkmkj网站开发费入账
  • 高端网站制作上海网站建设 交易保障
  • 网站中如何做图片轮播域名如何解析别人网站
  • 做雕塑设计的网站做网站需要什么 图片视频
  • wordpress资讯主题佛山seo优化
  • 本地企业网站建设服务做微信公众号海报的网站
  • 如何学习网站建设appwordpress pdf下载链接
  • 企业网站 建设公司长沙定制网站建设
  • 建设银行 北京招聘网站wordpress知更鸟打赏
  • 网站做自己的超链接杭州seo泽成
  • 做3d兼职网站凡科网是做什么的
  • 南海区住房城乡建设和水务局网站备案怎么关闭网站吗
  • 招远做网站软件工程开发师工资