Skip to content

DataAnnotationsValidator attempts to force render during renderer disposal resulting in circuit exception #32411

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

Closed
DamianEdwards opened this issue May 4, 2021 · 5 comments · Fixed by #35217
Assignees
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. Done This issue has been fixed feature-prerendering Issues related to prerendering blazor components
Milestone

Comments

@DamianEdwards
Copy link
Member

Found in version 6.0.100-preview.5.21254.10

Repro

  1. Create new Blazor Server app targeting .NET 6.0
  2. In "Index.razor" add the following block:
<EditForm Model="widget">
    <DataAnnotationsValidator />
    Widget Name: <InputText @bind-Value="widget.Name" />
</EditForm>

@code {
    Widget widget = new Widget();

    class Widget
    {
        public string Name { get; set; }
    };
}
  1. Run the app
  2. In the console output see the following exception:
fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HM8FAL8PSLLA", Request id "0HM8FAL8PSLLA:00000001": An unhandled exception was thrown by the application.
      System.InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.
         at Microsoft.AspNetCore.Components.Dispatcher.AssertAccess() in Microsoft.AspNetCore.Components.dll:token 0x60000b2+0x12
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(Int32 componentId, RenderFragment renderFragment) in Microsoft.AspNetCore.Components.dll:token 0x6000232+0x0
         at Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged() in Microsoft.AspNetCore.Components.dll:token 0x6000098+0x2d
         at Microsoft.AspNetCore.Components.Forms.InputBase`1.OnValidateStateChanged(Object sender, ValidationStateChangedEventArgs eventArgs) in Microsoft.AspNetCore.Components.Web.dll:token 0x6000150+0x6
         at Microsoft.AspNetCore.Components.Forms.EditContext.NotifyValidationStateChanged() in Microsoft.AspNetCore.Components.Forms.dll:token 0x6000018+0x16
         at Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.DataAnnotationsEventSubscriptions.Dispose() in Microsoft.AspNetCore.Components.Forms.dll:token 0x6000062+0x39
         at Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator.System.IDisposable.Dispose() in Microsoft.AspNetCore.Components.Forms.dll:token 0x600000b+0x0
         at Microsoft.AspNetCore.Components.Rendering.ComponentState.Dispose() in Microsoft.AspNetCore.Components.dll:token 0x60002cd+0x15
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(Boolean disposing) in Microsoft.AspNetCore.Components.dll:token 0x6000242+0x92
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception) in Microsoft.AspNetCore.Mvc.ViewFeatures.dll:token 0x6000060+0x0
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.<Dispose>g__NotifyExceptions|65_1(List`1 exceptions) in Microsoft.AspNetCore.Components.dll:token 0x600024a+0x2a
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(Boolean disposing) in Microsoft.AspNetCore.Components.dll:token 0x6000242+0xec
         at Microsoft.AspNetCore.Components.RenderTree.Renderer.DisposeAsync() in Microsoft.AspNetCore.Components.dll:token 0x6000244+0x9b
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.<DisposeAsync>g__Await|16_0(Int32 i, ValueTask vt, List`1 toDispose) in Microsoft.Extensions.DependencyInjection.dll:token 0x6000128+0x6e
         at Microsoft.AspNetCore.Http.Features.RequestServicesFeature.<DisposeAsync>g__Awaited|9_0(RequestServicesFeature servicesFeature, ValueTask vt) in Microsoft.AspNetCore.Http.dll:token 0x6000228+0x5b
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.<FireOnCompleted>g__ProcessEvents|227_0(HttpProtocol protocol, Stack`1 events) in Microsoft.AspNetCore.Server.Kestrel.Core.dll:token 0x6000b1e+0x81
@DamianEdwards DamianEdwards added area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. labels May 4, 2021
@DamianEdwards DamianEdwards changed the title DataAnnotationsValidator attempts to force render during component disposable resulting in circuit exception DataAnnotationsValidator attempts to force render during renderer disposal resulting in circuit exception May 5, 2021
@javiercn javiercn added the feature-prerendering Issues related to prerendering blazor components label May 5, 2021
@javiercn javiercn added this to the Next sprint planning milestone May 5, 2021
@ghost
Copy link

ghost commented May 5, 2021

Thanks for contacting us.

We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. 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.

@SteveSandersonMS
Copy link
Member

This issue made me a bit suspicious because I couldn't think of a reason why we'd be getting this error, but after a minute's investigation, it raises a wider question.

When we call a component's Dispose/DisposeAsync, do developers expect that call to arrive on the renderer's sync context? Normally it will be on the sync context because it will come out of a call chain that is on the sync context, e.g., in response to a navigation event when moving to another component. But when the "dispose" call chain starts outside all this, e.g., because you're disposing the renderer itself from external platform code, it looks as if nothing currently marshals the call onto the sync context, so we end up calling components' Dispose/DisposeAsync off the sync context.

This seems like a risky inconsistency to me, and could manifest in other ways than we see in this report. For example in cases like WebView, if your disposal logic involves interacting with the underlying native control in some way, you could get an error because you might not be on the UI thread.

I would think, then, that the renderer's Dispose logic should take care of using Dispatcher.Invoke around its calls into component Dispose/DisposeAsync methods. That would fix the wider class of issues.

cc @javiercn @Eilon since you've also been thinking about dispose logic in some detail recently. What do you think?

@javiercn
Copy link
Member

Yes, dispose needs to happen within the sync context. If we are not doing it, then I think we have a bug.

@Eilon
Copy link
Contributor

Eilon commented Jun 16, 2021

Yeah I might be naive, but I think that any time we call into some component-level API, it should be in the right context, right? I assume the same holds true for other component "events" such as Initialize, ParametersSet, etc.?

@SteveSandersonMS
Copy link
Member

Yes, sounds like we all agree. I'm pretty confident that other lifecycle methods already only ever get called on the sync context, as lots of things would be broken otherwise.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. Done This issue has been fixed feature-prerendering Issues related to prerendering blazor components
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants