aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

Controller.ViewComponent is not async

Open primozcerar opened this issue 2 years ago • 22 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the bug

Controller.ViewComponent writes synchronously to the HttpResponseStream if the content of the component is large enough causing the "System.InvalidOperationException: 'Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true "

Calling the same view from the controller with Controller.PartialView and the path to the component view or creating a wrapper view that calls @await Component.InvokeAsync avoids the issue. There are no synchronous renderpartial or partial calls in the view. The response size is 25kB.

This is the call stack of the exception:

   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count)
   at Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.FlushInternal(Boolean flushEncoder)
   at Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.Write(String value)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers.ViewBuffer.<WriteToAsync>d__23.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewComponentResultExecutor.<ExecuteAsync>d__7.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewComponentResultExecutor.<ExecuteAsync>d__7.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<<InvokeNextResultFilterAsync>g__Awaited|30_0>d`2.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<<InvokeResultFilters>g__Awaited|28_0>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<<InvokeNextResourceFilter>g__Awaited|25_0>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<<InvokeFilterPipelineAsync>g__Awaited|20_0>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<<InvokeAsync>g__Logged|17_1>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<<InvokeAsync>g__Logged|17_1>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<<Invoke>g__AwaitRequestTask|6_0>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Session.SessionMiddleware.<Invoke>d__8.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Session.SessionMiddleware.<Invoke>d__8.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.<HandleAsync>d__0.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.<Invoke>d__6.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.<Invoke>d__6.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

Example code with error:

        public virtual IActionResult DetailViewEdit(Dictionary<string, string> extraParam)
        {
            //DETAILVIEW
            var detailView = CreateViewEditInsertDetailView(ViewEditState.Edit, extraParam);

            return ViewComponent(ViewStrings.Components.DetailView, detailView);
        }

No error:

        public virtual IActionResult DetailViewEdit(Dictionary<string, string> extraParam)
        {
            //DETAILVIEW
            var detailView = CreateViewEditInsertDetailView(ViewEditState.Edit, extraParam);

            return PartialView(ViewStrings.Components.ComponentWrapperPath, new ComponentWrapperModel { Component = ViewStrings.Components.DetailView, Model = detailView });
        }
wrapper view:
@model ComponentWrapperModel

@await Component.InvokeAsync(Model.Component, Model.Model)

No error 2:

        public virtual IActionResult DetailViewEdit(Dictionary<string, string> extraParam)
        {
            //DETAILVIEW
            var detailView = CreateViewEditInsertDetailView(ViewEditState.Edit, extraParam);

            return PartialView("/Views/Shared/Components/DetailView/_Default.cshtml", detailView);
        }

Expected Behavior

As visible from the call stack ViewBuffer.WriteToAsync calls the Stream.Write method instead of Stream.WriteAsync. Why this only happens by calling Controller.ViewComponent I am not sure.

Steps To Reproduce

No response

Exceptions (if any)

System.InvalidOperationException: 'Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true

.NET Version

.net6

Anything else?

.NET SDK (reflecting any global.json): Version: 6.0.101 Commit: ef49f6213a

Runtime Environment: OS Name: Windows OS Version: 10.0.19043 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\6.0.101\

Host (useful for support): Version: 6.0.1 Commit: 3a25a7f1cc

.NET SDKs installed: 2.1.201 [C:\Program Files\dotnet\sdk] 2.1.202 [C:\Program Files\dotnet\sdk] 2.1.300 [C:\Program Files\dotnet\sdk] 2.1.401 [C:\Program Files\dotnet\sdk] 2.1.402 [C:\Program Files\dotnet\sdk] 2.1.526 [C:\Program Files\dotnet\sdk] 2.1.818 [C:\Program Files\dotnet\sdk] 2.2.103 [C:\Program Files\dotnet\sdk] 2.2.401 [C:\Program Files\dotnet\sdk] 2.2.402 [C:\Program Files\dotnet\sdk] 3.1.417 [C:\Program Files\dotnet\sdk] 5.0.104 [C:\Program Files\dotnet\sdk] 5.0.404 [C:\Program Files\dotnet\sdk] 5.0.406 [C:\Program Files\dotnet\sdk] 6.0.101 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.All 2.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.3-servicing-26724-03 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

primozcerar avatar Mar 29 '22 07:03 primozcerar

@primozcerar thanks for contacting us.

Can you create a minimal repro project as a public github repository that reproduces the issue?

javiercn avatar Mar 29 '22 09:03 javiercn

Hi @primozcerar. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost avatar Mar 29 '22 09:03 ghost

Not easily or quickly. I was hoping the stack trace would be enough to track down the issue. I have looked at the source code and I suspect the problem is here: https://github.com/dotnet/aspnetcore/blob/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Mvc/Mvc.ViewFeatures/src/Buffers/ViewBuffer.cs#L258

I can not be sure of course.

primozcerar avatar Mar 29 '22 09:03 primozcerar

Thank you for filing this issue. In order for us to investigate this issue, please provide a minimalistic repro project (ideally a GitHub repo) that illustrates the problem.

mkArtakMSFT avatar Mar 29 '22 16:03 mkArtakMSFT

Hi @primozcerar. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost avatar Mar 29 '22 16:03 ghost

Unfortunately I am unable to reproduce the error in a simple project. There must be some combination of settings or view hierarchy that causes this. I still believe the problem is in calling the synchronous write from the ViewBuffer WriteToAsync method as is visible in the call stack. I realize IHtmlContent does not provide a WriteAsync method but then it should be handled differently.

primozcerar avatar Apr 01 '22 10:04 primozcerar

@primozcerar do you happen to have any @FlushAsync() call on your page?

It's very strange, as MVC will buffer all the response by default.

javiercn avatar Apr 04 '22 12:04 javiercn

Hi @primozcerar. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost avatar Apr 04 '22 12:04 ghost

No @FlushAsync() in the project.

primozcerar avatar Apr 04 '22 12:04 primozcerar

All I can add is that I use Devexpress controls extensively in the project and possibly there is something in their code that would lead to this issue, but the root problem seen in the call stack still remains. I also did not see any reports in their support ticket system about this problem.

primozcerar avatar Apr 04 '22 12:04 primozcerar

@primozcerar thanks for the additional details.

It might help if you can figure out what specific ViewComponent causes the issue to begin with (what VC creates that content). That said, unfortunately, without a minimal repro is impossible for us to make progress on this issue.

javiercn avatar Apr 04 '22 13:04 javiercn

Hi @primozcerar. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost avatar Apr 04 '22 13:04 ghost

Hi! Hopefully I'm not hijacking this issue, but I ran into what looks like the same thing this morning on a project that was recently upgraded from .NET 5.0 to 6.0 *.

The original code is something we inherited so it may very well be doing something ill-advised, but it was working before and isn't now, so here I am! 😃

A very basic repro should be visible in this repo: https://github.com/welshronaldo/ViewCompTest

That's just a default new web project with a ViewComponent used in a similar manner to what we've got in the real project. The component in this case just returns a select tag with "N" GUIDs, specified in the numerical input (the real one is loading things from a database, inevitably). It's fine up to 163 items, but breaks with the same exception detailed above at 164. For the sake of the repro, I'm just dumping that raw response into the DOM.

The issue from our perspective is easily worked around in the meantime, and we probably need to reassess what we're doing in our specific case, but hopefully this sheds some light anyway.

Thanks for your time!

*Edit: After further review, the .NET version upgrade itself is unrelated and just coincided with the content in the component having grown in the intervening time. I'm able to reproduce the behavior (on a repro like the one linked) as far back as netcoreapp3.1.

leftnet avatar Apr 04 '22 15:04 leftnet

Just following up with the apparent solution for our specific case:

Replacing the old-style:

@Html.DropDownList("listGuids", Model, new { @class = "form-control" })

with:

<select asp-items="Model" id="listGuids" class="form-control"></select>

... resolves the issue. No idea if that's related to what you're dealing with @primozcerar, but best of luck with it!

leftnet avatar Apr 04 '22 17:04 leftnet

@welshronaldo Thank you for this. I appreciate it since I couldn't reproduce the issue in a simple project. Hopefully this helps solve it.

Edit: Your repro project seems to show the same issue judging from the call stack of the exception. I even tried to change the TestComponent.cs in your project to use InvokeAsync just in case that changes anything but the exception remains.

primozcerar avatar Apr 05 '22 06:04 primozcerar

Thanks for contacting us. We're moving this issue to the .NET 7 Planning milestone for future evaluation / consideration. Because it's not immediately obvious that this is a bug in our framework, we would like to keep this around to collect more feedback, which can later help us determine the impact of it. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

ghost avatar Apr 05 '22 11:04 ghost

I don't understand how you can say that it is not immediately obvious that this is a bug in asp.net core? The project welshronaldo provided uses nothing but asp.net core framework so the bug can not be from somewhere else. All calls are made async yet the exception is thrown. I understand the need to prioritize but this is quite obviously a bug.

primozcerar avatar Apr 05 '22 11:04 primozcerar

@primozcerar the comment our bot made might not be completely accurate in this case. This is simply part of our planning process, we are acknowledging that there is very likely an issue here. However an engineer from our team needs to spend time looking at it to understand the details and produce a detailed explanation of what's going on before we decide whether this is a bug, a bad design or a technical limitation and then decide what next steps we should take.

javiercn avatar Apr 05 '22 11:04 javiercn

I think I'm hitting exactly the same issue. Invoking a VC directly from a Controller action method using
return ViewComponent(...

crashes with error

System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead. at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count) at Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.FlushInternal(Boolean flushEncoder) at Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.Write(String value)

It works fine most of the time, but in one specific case the view component returns html containing a large json object - in this case data for a Kendo dropdown list which contains nearly 1000 items.

I'm sure there are work-rounds for this - e.g. enabling synchronous IO, but that's not the point.

robinsmh avatar Jun 29 '22 08:06 robinsmh

I've come across this issue too.

I've managed to create a simple reproduction. It has to do with the length of content returned.

namespace WebApplication1.Controllers;

using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc;

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return this.ViewComponent(typeof(WorkingViewComponent));
    }

    public IActionResult Fail()
    {
        return this.ViewComponent(typeof(FailingViewComponent));
    }
}

public class WorkingViewComponent : ViewComponent
{
    public IHtmlContent Invoke()
    {
        return new HtmlString(new String('X', 15000));
    }
}

public class FailingViewComponent : ViewComponent
{
    public IHtmlContent Invoke()
    {
        return new HtmlString(new String('X', 20000));
    }
}

Version: 6.0.302 Commit: c857713418

psylenced avatar Aug 01 '22 08:08 psylenced

ping @javiercn

psylenced avatar Aug 01 '22 09:08 psylenced

We face the same problem too.

antonin-mercay avatar Aug 11 '22 07:08 antonin-mercay

I've managed to create a simple reproduction. It has to do with the length of content returned.

That's right, It's related to the length of the view content.

maliming avatar Aug 15 '22 08:08 maliming

@TanayParikh can you please investigate this and summarize your findings and any actions to be taken for resolving this issue? Thanks!

mkArtakMSFT avatar Nov 01 '22 22:11 mkArtakMSFT

I followed the reproduction provided by @psylenced then found the threshold is 16K.

public class FailingViewComponent : ViewComponent
{
    public IHtmlContent Invoke()
    {
        return new HtmlString(new string('X', 16385)); // 16384 is OK
    }
}

I searched 16 * 1024 and found the DefaultBufferSize in MemoryPoolHttpResponseStreamWriterFactory.cs#L28

public const int DefaultBufferSize = 16 * 1024;

I tried to implement CustomMemoryPoolHttpResponseStreamWriterFactory to change the DefaultBufferSize to 32K.

public class CustomMemoryPoolHttpResponseStreamWriterFactory : IHttpResponseStreamWriterFactory
{
    public const int DefaultBufferSize = 32 * 1024;

    private readonly ArrayPool<byte> _bytePool;
    private readonly ArrayPool<char> _charPool;

    public CustomMemoryPoolHttpResponseStreamWriterFactory(
        ArrayPool<byte> bytePool,
        ArrayPool<char> charPool)
    {
        _bytePool = bytePool;
        _charPool = charPool;
    }

    public TextWriter CreateWriter(Stream stream, Encoding encoding)
    {
        return new HttpResponseStreamWriter(stream, encoding, DefaultBufferSize, _bytePool, _charPool);
    }
}

Replacing MemoryPoolHttpResponseStreamWriterFactory with CustomMemoryPoolHttpResponseStreamWriterFactory.

builder.Services.TryAddSingleton<IHttpResponseStreamWriterFactory, CustomMemoryPoolHttpResponseStreamWriterFactory>();

It worked as exptected.

// FailingViewComponent no longer failed
public class FailingViewComponent : ViewComponent
{
    public IHtmlContent Invoke()
    {
        // 16385 is OK with CustomMemoryPoolHttpResponseStreamWriterFactory
        return new HtmlString(new string('X', 16385)); 
    }
}

cnblogs-dudu avatar Mar 01 '23 12:03 cnblogs-dudu

That is not a solution in my opinion. It should be async but it isn't.

primozcerar avatar Mar 01 '23 13:03 primozcerar

It works with async.

public class HomeController : Controller
{
    public IActionResult Fail()
    {
        return ViewComponent(typeof(FailingViewComponent));
    }
}

// FailingViewComponent no longer failed
public class FailingViewComponent : ViewComponent
{
    public Task<IHtmlContent> InvokeAsync()
    {
        // 16385 is OK with CustomMemoryPoolHttpResponseStreamWriterFactory
        return Task.FromResult<IHtmlContent>(new HtmlString(new string('X', 16385)));
    }
}

cnblogs-dudu avatar Mar 01 '23 14:03 cnblogs-dudu

And does it then accept more than the buffer size without error? More than 32kB in your case.

primozcerar avatar Mar 01 '23 14:03 primozcerar

No. The DefaultBufferSize is the limit.

cnblogs-dudu avatar Mar 01 '23 14:03 cnblogs-dudu

And that is the problem that needs to be solved.

primozcerar avatar Mar 01 '23 14:03 primozcerar