Razor component in blocks fails with an InvalidOperationException when rendered using PropertyFor

Vote:
 

I know that this is an extreme edge case but figured that anyone that's as curious as we are on Blazor and Razor components might run into this issue.

If you happen to build a Block that will end up rendering a Razor component with server interactivity you may end up with the following exception and stacktrace:

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()
         at Microsoft.AspNetCore.Components.HtmlRendering.Infrastructure.StaticHtmlRenderer.WriteComponentHtml(Int32 componentId, TextWriter output)
         at Microsoft.AspNetCore.Components.Endpoints.EndpointHtmlRenderer.WriteComponentHtml(Int32 componentId, TextWriter output, Boolean allowBoundaryMarkers, SequenceAndKey sequenceAndKey)
         at Microsoft.AspNetCore.Components.Endpoints.EndpointHtmlRenderer.WriteComponentHtml(Int32 componentId, TextWriter output)
         at Microsoft.AspNetCore.Components.Endpoints.EndpointHtmlRenderer.PrerenderedComponentHtmlContent.WriteTo(TextWriter writer, HtmlEncoder encoder)
         at Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers.ViewBuffer.WriteTo(TextWriter writer, HtmlEncoder encoder)
         at EPiServer.Web.Mvc.Html.Internal.WrappedHtmlContent.WriteTo(TextWriter writer, HtmlEncoder encoder)
         at Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers.ViewBuffer.WriteToAsync(TextWriter writer, HtmlEncoder encoder)
         at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter)
         at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter)
         at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
         at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)
         at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         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()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         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()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
         at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
         at EPiServer.Cms.Shell.UI.Internal.RegisterAdminUserMiddleware.InvokeAsync(HttpContext context)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
         at EPiServer.Middleware.InitializeOnFirstRequestMiddleware.InvokeAsync(HttpContext httpContext)
         at EPiServer.Framework.DependencyInjection.Internal.VisitorGroupMiddleware.Invoke(HttpContext httpContext)
         at Microsoft.WebTools.BrowserLink.Net.BrowserLinkMiddleware.InvokeAsync(HttpContext context)
         at Microsoft.AspNetCore.Watch.BrowserRefresh.BrowserRefreshMiddleware.InvokeAsync(HttpContext context)
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

This issue seems to occur when you're rendering this block in a contentarea using @Html.PropertyFor(m => m.MyContentArea).
If you change @Html.PropertyFor to @Html.DisplayFor or use the new epi-property-taghelper - this problem does not take place.

Just to clarify, a block vith a view like this:
CounterBlock.cshtml

@model CounterBlock

<h3>Hello Counter Block</h3>
<component type="typeof(Counter)" render-mode="ServerPrerendered" />

cannot be rendered using @Html.PropertyFor.

ArticlePage.cshtml

@* This works *@
<div epi-property="CurrentContent.MainBody" />

@* This works *@
@Html.DisplayFor(x => x.CurrentContent.MainBody)

@* This also works *@
@{ Html.RenderContentArea(Model.CurrentContent.MainBody); }

@* But this does not work *@
@Html.PropertyFor(x => x.CurrentContent.MainBody)


I pushed my examples to a new empty CMS site if anyone wants run this themselves: https://github.com/KevinJCandlert/epi-razor-component-issue/tree/master


#319644
Edited, Mar 28, 2024 22:16
Karol Berezicki - Mar 29, 2024 8:53
Hi Kevin, just wanted to let you know that the repository seems to be private.
Yet I was able to reproduce it in my project.
Kevin Candlert - Mar 30, 2024 19:59
oh shoot! It's fixed now, thanks! 😄
Ted - May 27, 2024 9:15
Thanks for sharing! We see the same behavior after upgrading to .NET 8.
Vote:
 

This thread helped me a lot! I was just about to rewrite my code and use a View component instead, when I found this!

Have found found any answers to why PropertyFor doesn't work?

#321705
May 08, 2024 11:58
Vote:
 

No, We haven't implemented more Blazor/Razor components since then. Even though we'd like to do more blazor things it's a little too experimental for production for us right now.
However, the blocks we previously made are still in use and working. We render those in content areas with @Html.DisplayFor( ... )

#321706
May 08, 2024 12:06
Ted
Vote:
 

I managed to get it working without too much of a hack: https://tedgustaf.com/blog/2024/blazor-in-optimizely-cms-12-with-.net-8

think it's a viable workaround until Optimizely fixes the core issue.

#322870
May 30, 2024 9:39
Mattias Brage - May 30, 2024 10:35
Awesome Ted! I will definitely try it out!
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.