タイトルのケースはかなり特定されていますが、エラー原因自体は、異なるRender Mode間の情報受け渡しに関するもので、学びが深かったので記事にしました。
環境
VS2022のBlazor Appテンプレートからプロジェクトを作成します。
Render ModeはWebAssemblyを選択してください。
エラーを再現
実装
- Layout
ClientプロジェクトにLayout Componentを作成します。
@inherits LayoutComponentBase
<h1>ClientMainLayout</h1>
@Body
@inherits LayoutComponentBase
@* 以下の参照をしたいので、DefaultとなるLayoutをClientプロジェクトに移動しています。*@
@layout ClientMainLayout
@* そしてWebAssembly Componentとしたい *@
@rendermode InteractiveWebAssembly
<h2>SpecificLayout</h2>
@Body
- Component
上記のLayoutを参照するPage Componentを生成します。
@page "/component"
@layout BlazorAppError.Client.Components.Layout.SpecificLayout
<h3>Component</h3>
- Routes
次に、Mainプロジェクト側のRoutes.razor
のDefaultLayout
に上記のComponentを指定します。
ちなみに、デフォルトでMainプロジェクトはClientプロジェクトをDependencisに入れているため以下の参照が可能となっています。
<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Client._Imports).Assembly }">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Client.Components.Layout.ClientMainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
実行
RootではClientMainLayout.razor
が問題なく反映されていることが分かります。
そこから、/component
に遷移すると、エラーが発生しますね。
An unhandled exception occurred while processing the request.
InvalidOperationException: Cannot pass the parameter 'Body' to component 'SpecificLayout' with rendermode 'InteractiveWebAssemblyRenderMode'. This is because the parameter is of the delegate type 'Microsoft.AspNetCore.Components.RenderFragment', which is arbitrary code and cannot be serialized.
原因
ポイントは2つです。
- Static ServerとInteractive WebAssemblyのRender Mode
@Body
をCascadingしている -
@Body
はシリアライズできないContent
公式ドキュメントに述べられているケースに該当します。
@Body
の内容はJsonシリアライズできないため、Static ServerとInteractive WebAssebly間ではやり取りできないということです。上記のエラーにも"is arbitrary code and cannot be serialized"と書いていますね。
対応方法
幸いにも、@Body
に対して何かを積み重ねる必要がなかったので、@rendermode InteractiveWebAssembly
が必要なComponentを別で生成し、対象のLayoutから呼ぶという方法で回避できました。
エラー再現コードを修正すると以下のようになります。
@rendermode InteractiveWebAssembly
<h2>SpecificLayout</h2>
上記で生成したComponentをLayoutから呼び出す。
@inherits LayoutComponentBase
@layout ClientMainLayout
<SpecificComponent />
@Body
実行するとエラー無しで想定通りの動きができていますね。
最後に
このエラーに遭遇したときは、Layout Componentに@rendermode
は適用できないのかと諦めていました。
色々試しているうちに、対応方法にたどり着き、暇な時間にドキュメントを読んでいて「これだ!」となった感じです。
Blazorは置く深すぎるので、ドキュメント流し読みも効果ありです!