Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Controller.ViewComponent is not async #40928

Open
1 task done
primozcerar opened this issue Mar 29, 2022 · 38 comments
Open
1 task done

Controller.ViewComponent is not async #40928

primozcerar opened this issue Mar 29, 2022 · 38 comments
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-mvc-razor-views Features related to the Razor view engine for Razor pages and MVC views investigate Priority:1 Work that is critical for the release, but we could probably ship without
Milestone

Comments

@primozcerar
Copy link

primozcerar commented Mar 29, 2022

Is there an existing issue for this?

  • 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]

@javiercn javiercn added area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-mvc-razor-views Features related to the Razor view engine for Razor pages and MVC views labels Mar 29, 2022
@javiercn
Copy link
Member

@primozcerar thanks for contacting us.

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

@javiercn javiercn added the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label Mar 29, 2022
@ghost
Copy link

ghost commented Mar 29, 2022

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.

@primozcerar
Copy link
Author

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:

valueAsHtmlContent.WriteTo(writer, encoder);

I can not be sure of course.

@ghost ghost added Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. and removed Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels Mar 29, 2022
@mkArtakMSFT
Copy link
Member

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 mkArtakMSFT added Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. and removed Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. labels Mar 29, 2022
@ghost
Copy link

ghost commented Mar 29, 2022

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.

@primozcerar
Copy link
Author

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.

@ghost ghost added Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. and removed Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels Apr 1, 2022
@javiercn
Copy link
Member

javiercn commented Apr 4, 2022

@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 javiercn added Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. and removed Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. labels Apr 4, 2022
@ghost
Copy link

ghost commented Apr 4, 2022

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.

@primozcerar
Copy link
Author

No @FlushAsync() in the project.

@ghost ghost added Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. and removed Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels Apr 4, 2022
@primozcerar
Copy link
Author

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.

@javiercn
Copy link
Member

javiercn commented Apr 4, 2022

@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 javiercn added Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. and removed Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. labels Apr 4, 2022
@ghost
Copy link

ghost commented Apr 4, 2022

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.

@leftnet
Copy link

leftnet commented Apr 4, 2022

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
Copy link

leftnet commented Apr 4, 2022

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!

@primozcerar
Copy link
Author

primozcerar commented Apr 5, 2022

@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.

@maliming
Copy link

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.

@mkArtakMSFT
Copy link
Member

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

@cnblogs-dudu
Copy link
Contributor

cnblogs-dudu commented Mar 1, 2023

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

@primozcerar
Copy link
Author

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

@cnblogs-dudu
Copy link
Contributor

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

@primozcerar
Copy link
Author

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

@cnblogs-dudu
Copy link
Contributor

No. The DefaultBufferSize is the limit.

@primozcerar
Copy link
Author

And that is the problem that needs to be solved.

@cnblogs-dudu
Copy link
Contributor

ViewBuffer.cs#L98 could result in sync WriteTo. The EncodingWrapper implements IHtmlContent, see ViewBuffer.cs#L388

AppendValue(new ViewBufferValue(new EncodingWrapper(unencoded)));

ViewBuffer.cs#L244

if (value.Value is string valueAsString)
{
    await writer.WriteAsync(valueAsString);
    continue;
}

if (value.Value is ViewBuffer valueAsViewBuffer)
{
    await valueAsViewBuffer.WriteToAsync(writer, encoder);
    continue;
}

if (value.Value is IHtmlContent valueAsHtmlContent)
{
    valueAsHtmlContent.WriteTo(writer, encoder);
    await writer.FlushAsync();
    continue;
}

value.Value is ViewBufferValue.Value

@kjbetz
Copy link

kjbetz commented Jun 19, 2023

I believe I'm also encountering this issue as well.

I'm attempting to use ASP.NET Core 8 Preview 5 with the new Blazor full-stack / SSR. I have a page that just has static HTML in it (a privacy page.) Formatted HTML is about 460 lines. I get this error. If I lower the lines to around 240 the page loads showing about 16kb data from this page.

Is this the same issue? Is there a work around?

@MisinformedDNA
Copy link
Contributor

Yep, I have a Blazor 8 (preview 6) page with static HTML and if there's "too much" text, I get the error. Reducing the text size fixes it. It's not even that much text! How do I get Blazor to work? this is a very simple app that Blazor should easily be able to support. @mkArtakMSFT @danroth27

@lanedirt
Copy link

lanedirt commented Jul 17, 2023

@kjbetz @MisinformedDNA
I have ran into this issue as well with Blazor .NET 8 preview 6. If the body contains too much text, the page won't load and it throws an error. I created a separate issue for this here (before I saw your comments):
#49466

@query-wow
Copy link

query-wow commented Oct 29, 2023

I'm having the same issue invoking a big view component. I have some sync view components inside it as well but even turning them async doesn't do the trick.

Edit: I'm using a asp.net core web app

@primozcerar
Copy link
Author

It seems some work has been done regarding this issue on the Blazor side #49172 .
Will this be ported to asp.net core?

@captainsafia captainsafia modified the milestones: .NET 8 Planning, Backlog Mar 1, 2024
@LaughingJohn
Copy link

Just wanted to add my name to the list. Same issue but with a partial in Asp.Net core razor pages.

@cvdsoftware
Copy link

cvdsoftware commented Apr 9, 2024

Recently I encountered the same problem. I've a ViewComponent that's returning a big HtmlString which is coming from the DB.

The good news is that .NET 8 introduced the IHtmlAsyncContent interface for Blazor in this #47117 pull request.

So if you just want to return a big HtmlString from a ViewComponent you can fix it this way:

public class HtmlAsyncString : HtmlString, IHtmlAsyncContent
{
    public HtmlAsyncString(string? value) : base(value) { }

    public async ValueTask WriteToAsync(TextWriter writer)
    {
        ArgumentNullException.ThrowIfNull(writer);

        await writer.WriteAsync(Value);
    }
}
public class FailedBeforeViewComponent : ViewComponent
{
    public IHtmlAsyncContent Invoke()
    {
        return new HtmlAsyncString(new string('X', 20000));  // Does not fail anymore
    }
}

// Also compatible with IHtmlContent
public class FailedBeforeViewComponent : ViewComponent
{
    public IHtmlContent Invoke()
    {
        return new HtmlAsyncString(new string('X', 20000));  // Does not fail anymore
    }
}

// async example:
public class FailedBeforeViewComponent : ViewComponent
{
    public async Task<IHtmlContent> InvokeAsync()
    {
        await Task.Delay(1); // Just a demo
        return new HtmlAsyncString(new string('X', 20000)); // Does not fail anymore
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-mvc-razor-views Features related to the Razor view engine for Razor pages and MVC views investigate Priority:1 Work that is critical for the release, but we could probably ship without
Projects
None yet
Development

No branches or pull requests