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
Comments
@primozcerar thanks for contacting us. Can you create a minimal repro project as a public github repository that reproduces the issue? |
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. |
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:
I can not be sure of course. |
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. |
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. |
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 do you happen to have any It's very strange, as MVC will buffer all the response by default. |
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. |
No @FlushAsync() in the project. |
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 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. |
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. |
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. |
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! |
@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. |
That's right, It's related to the length of the view content. |
@TanayParikh can you please investigate this and summarize your findings and any actions to be taken for resolving this issue? Thanks! |
I followed the reproduction provided by @psylenced then found the threshold is public class FailingViewComponent : ViewComponent
{
public IHtmlContent Invoke()
{
return new HtmlString(new string('X', 16385)); // 16384 is OK
}
} I searched public const int DefaultBufferSize = 16 * 1024; I tried to implement 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 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));
}
} |
That is not a solution in my opinion. It should be async but it isn't. |
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)));
}
} |
And does it then accept more than the buffer size without error? More than 32kB in your case. |
No. The |
And that is the problem that needs to be solved. |
ViewBuffer.cs#L98 could result in sync AppendValue(new ViewBufferValue(new EncodingWrapper(unencoded))); 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;
}
|
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? |
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 |
@kjbetz @MisinformedDNA |
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 |
It seems some work has been done regarding this issue on the Blazor side #49172 . |
Just wanted to add my name to the list. Same issue but with a partial in Asp.Net core razor pages. |
Recently I encountered the same problem. I've a The good news is that .NET 8 introduced the So if you just want to return a big 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
}
} |
Is there an existing issue for this?
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:
Example code with error:
No error:
No error 2:
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]
The text was updated successfully, but these errors were encountered: