.NET 6中RenderAsync调用IViewBufferScope时索引越界问题排查
问题:Razor视图渲染字符串时偶发索引越界异常
在.NET 6环境下,我通过IView将视图渲染为字符串作为邮件内容,99.9%的场景运行正常,但每日会出现3-4次索引越界异常。
相关实现代码
using System; using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Routing; namespace MyRazor.Namespace { public class MyRazor : IMyRazor { private readonly IRazorViewEngine _viewEngine; private readonly ITempDataProvider _tempDataProvider; private readonly IServiceProvider _serviceProvider; public MyRazor( IRazorViewEngine viewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider) { _viewEngine = viewEngine; _tempDataProvider = tempDataProvider; _serviceProvider = serviceProvider; } public async Task<string> RenderViewToStringAsync<TModel>(string viewPath, TModel model, Action<dynamic> initializeViewBag = null) { ActionContext actionContext = new ActionContext(new DefaultHttpContext { RequestServices = _serviceProvider }, new RouteData(), new ActionDescriptor()); IView view = _viewEngine.FindView(actionContext, viewPath, isMainPage: false).View; using (StringWriter writer = new StringWriter()) { ViewContext viewContext = new ViewContext( actionContext, view, new ViewDataDictionary<TModel>( metadataProvider: new EmptyModelMetadataProvider(), modelState: new ModelStateDictionary()) { Model = model, }, new TempDataDictionary( actionContext.HttpContext, _tempDataProvider), writer, new HtmlHelperOptions()); initializeViewBag?.Invoke(viewContext.ViewBag); await view.RenderAsync(viewContext); return writer.ToString(); } } } }
IMyRazor被注册为瞬态服务,触发的异常信息如下:
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
at System.Collections.Generic.List`1.RemoveAt(Int32 index)
at Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers.MemoryPoolViewBufferScope.GetPage(Int32 pageSize)
at Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers.ViewBuffer.AppendNewPage()
我推测由于IViewBufferScope是作用域服务,当并行调用IMyRazor.RenderViewToStringAsync时,同一作用域下的多个视图上下文产生干扰导致异常,但无法通过测试复现该问题。请问如何复现上述异常?
测试代码
// xunit [Fact] public async Task TestFailure() { using (IServiceScope scope = testFixture.TestServer.Host.Services.CreateScope()) { // Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers.IViewBufferScope is scoped // IMyRazor is transient IMyRazor builder = scope.ServiceProvider.GetRequiredService<IMyRazor>(); List<Task<string>> tasks = new List<Task<string>>(); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test", new { })); tasks.Add(builder.RenderViewToStringAsync("test2", payload)); await Task.WhenAll(tasks); foreach (var task in tasks) { Assert.True(task.IsCompletedSuccessfully); } } }
内容的提问来源于stack exchange,提问作者SilentTremor
相关产品推荐
相关产品推荐

