havit / Havit.Blazor

Free Bootstrap 5 components for ASP.NET Blazor + optional enterprise-level stack for Blazor development (gRPC code-first, layered architecture, localization, auth, ...)
https://havit.blazor.eu
MIT License
490 stars 67 forks source link

[HxTabPanel] Exception on page refresh #107

Closed Apskaita5 closed 2 years ago

Apskaita5 commented 2 years ago

If you use async parameter setting in OnParametersSetAsync or OnInitializedAsync, e.g.

protected override async Task OnInitializedAsync() { try { await Task.Delay(100); forecasts = new List(); } catch (Exception ex) { MessengerService.AddError(ex.Message); } }

and have a HxTabPanel on the component, on page refresh you get an excpetion: "InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state."

No problems with other Havit components I tried.

I can solve the problem by moving async load operation to OnAfterRenderAsync. However, as it only happens with the HxTabPanel, I believe something is wrong with its implementation.

hakenr commented 2 years ago

@Apskaita5 Can you provide us with a complete exception call stack?

Apskaita5 commented 2 years ago

An unhandled exception occurred while processing the request. AggregateException: Exceptions were encountered while disposing components. (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception)

InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess()

InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess()

InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess()

InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess()

InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess()

InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess()

Stack Query Cookies Headers Routing AggregateException: Exceptions were encountered while disposing components. (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) (The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.) Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception) Microsoft.AspNetCore.Components.RenderTree.Renderer.gNotifyExceptions|52_1(List exceptions) Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(bool disposing) Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose() Microsoft.AspNetCore.Mvc.ViewFeatures.StaticComponentRenderer.PrerenderComponentAsync(ParameterView parameters, HttpContext httpContext, Type componentType) Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.PrerenderedServerComponentAsync(HttpContext context, ServerComponentInvocationSequence invocationId, Type type, ParameterView parametersCollection) Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.RenderComponentAsync(ViewContext viewContext, Type componentType, RenderMode renderMode, object parameters) Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output) Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.g__Awaited|0_0(Task task, TagHelperExecutionContext executionContext, int i, int count) HavitBlazorTest.Pages.PagesHost.b__18_1() in _Host.cshtml +

Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync() HavitBlazorTest.Pages.PagesHost.ExecuteAsync() in _Host.cshtml + Layout = null; Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context) Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts) Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context) Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable statusCode) Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable statusCode) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.gAwaited|27_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.gAwaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.gAwaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Show raw exception details InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess() Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(int componentId, RenderFragment renderFragment) Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged() Havit.Blazor.Components.Web.CollectionRegistration.Unregister(TItem item) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose(bool disposing) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose() Microsoft.AspNetCore.Components.Rendering.ComponentState.Dispose() Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(bool disposing)

Show raw exception details InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess() Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(int componentId, RenderFragment renderFragment) Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged() Havit.Blazor.Components.Web.CollectionRegistration.Unregister(TItem item) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose(bool disposing) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose() Microsoft.AspNetCore.Components.Rendering.ComponentState.Dispose() Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(bool disposing)

Show raw exception details InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess() Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(int componentId, RenderFragment renderFragment) Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged() Havit.Blazor.Components.Web.CollectionRegistration.Unregister(TItem item) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose(bool disposing) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose() Microsoft.AspNetCore.Components.Rendering.ComponentState.Dispose() Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(bool disposing)

Show raw exception details InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess() Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(int componentId, RenderFragment renderFragment) Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged() Havit.Blazor.Components.Web.CollectionRegistration.Unregister(TItem item) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose(bool disposing) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose() Microsoft.AspNetCore.Components.Rendering.ComponentState.Dispose() Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(bool disposing)

Show raw exception details InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess() Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(int componentId, RenderFragment renderFragment) Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged() Havit.Blazor.Components.Web.CollectionRegistration.Unregister(TItem item) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose(bool disposing) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose() Microsoft.AspNetCore.Components.Rendering.ComponentState.Dispose() Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(bool disposing)

Show raw exception details InvalidOperationException: The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state. Microsoft.AspNetCore.Components.Dispatcher.AssertAccess() Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(int componentId, RenderFragment renderFragment) Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged() Havit.Blazor.Components.Web.CollectionRegistration.Unregister(TItem item) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose(bool disposing) Havit.Blazor.Components.Web.Bootstrap.HxGridColumnBase.Dispose() Microsoft.AspNetCore.Components.Rendering.ComponentState.Dispose() Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(bool disposing)

hakenr commented 2 years ago

Is seems the exception comes from the HxGridColumnBase.Dispose() method where there is StateHasChanged() callback and in server-prerendering mode, under some circumstances, the Dispose method is called out of Dispatcher thread.

@jirikanda Can you look into it? ...it is probably the StateHasChanged in component-registration we touched yesterday. Wrapping with InvokeAsync() might help?

jirikanda commented 2 years ago

Hi guys,

I read the callstack the same way:

@hakenr It could be connected with ConfigureAwait we also discussed yesterday.

Unfortunatelly, I am not able to reproduce the issue. Reproducing the issue is necessary to verify the fix. @Apskaita5, could you provide more information, please? We need reprosteps like we did for another issue, ie. Issue102Test.razor Alternatively, you can provide a small git repository with reprosteps.

Apskaita5 commented 2 years ago

I was unable to reproduce the issue when writing from scratch too :) I guess I created some very exotic mix in my test project (attached). HavitBlazorTest.zip

jirikanda commented 2 years ago

@Apskaita5, thank you very much for your reprosteps.

I analyzed the attached code and successfully reproduced the issue. It is quite tricky while to reproduce the issue:

  1. There must be EditForm, Model must change value between renders (it is the most strange part of the reprosteps (@(forecasts ?? new List<WeatherForecast>()) in your code)).
  2. There must be HxGrid with HxGridColumn.
  3. There must be OnInitializedAsync which returns uncompleted Task (which adds additional render).
  4. .NET 5 as target framework (in .NET 6 the issue is not reproduced anymore).

While the problem is in the 2nd point, I guess the points 1 and 3 are "the initiators" of the problem and I hope they will lead me to the source of the problem.

@hakenr, ConfigureAwait mentioned before is not connected to the issue.

To be continued...

jirikanda commented 2 years ago

Fixed.