.NET Blazor Serverアプリをストップさせる2つの原因
株式会社ジェイテックジャパン CTOの高丘 @tomohisaです。この記事では、C#でフロントエンドを開発できるBlazorでつまづいた点を備忘として残しておきます。
最近、私たちジェイテックジャパンで開発しているC#によるイベントソーシング・CQRSフレームワークSekibanおよびC#による鉄道指向プログラミングライブラリ ResultBoxのサンプルとして、簡単な勤怠管理プログラムを作成しています。
これを作成するにあたり、最近.NETの開発のためのスタックとしてマイクロソフトが開発している、Aspire を使用して開発しているのですが、Aspireのデフォルトのフロントエンド開発環境が Blazor という、C#でウェブアプリケーションを記述するフレームワークで、使ってみたいと思っていたので、勉強がてら使ってみています。
ただ、最初はかなりクセがあり、使いにくく、問題に当たった時に問題の切り出しが面倒だったので、書いておきたいと思います。
1. サーバーサイドでの実行の際に例外を投げてはいけない
BlazorはWebアセンブリーでブラウザのみで動かす、Blazor WebAssembly と、サーバーサイドとブラウザで交互に動かしつつ、データをSignalRでを経由したWebSocketで接続するBlazor Serverの2つが大きくあり、それらを両方ページごとに使うことのできる、Hybridモードもあります。今回は学習のために基本的にBlazor Serverを使用しています。
Blazor Serverを使っている場合、イベントやレンダリングで、サーバー側に接続する時に @ をつけることによって、サーバーサイドの処理であることを明示することができます。ただ、サーバーサイドで動いていても、クライアント側のJSを呼び出すための機構があったり、リンクの移動の機能があったりして便利なものの、どのように動いているかの予測がWebAPI+SPAの場合のように簡単にできないという問題があります。
今回、Blazor Serverを使って作ったアプリが動かなくなるという問題が発生しました。あるページを開くと、リンクやボタンも効かなくなり、フロントエンドが止まったように感じました。調べてみると、バックエンドで問題が起こったらバックエンドとフロントエンドの接続がうまくいかなくなるため、フロントエンドからバックエンドを @ を使って読んでいる部分全てで動作しなくなる、クラッシュするという問題であるということがわかりました。
ここで説明されているように、バックエンド側の操作でException(例外)が throwされた時に、SignalRが動作するWebSocketに異常が起き、クライアントとサーバー側が接続できなくなるようです。WebAPIでデータを撮るときはとサーバーが500エラーが起きてもフロントの動作は壊れないので、まずここで躓きました。
解決策としては、try / catchで括ってthrowされなくすることにより解決しました。
2. サーバーサイドのデータ表示部分で .Wait や .Resultによる非同期処理のブロックをしない
これもそのままと言ったらそのままなのですが、上記1.のthrow Exceptionをしてもまだ動作が止まってしまうケースが存在しました。それは、データ表示の際に @(task).Result のように非同期処理から取得したデータを画面に表示しようとしていたせいでした。
この動作によってどうもロックがかかってしまうようで、クラッシュログも表示されずにストップしてしまうので、切り分け作業が面倒でした。
解決策としては、コードの関数部分で、awaitして、privateの変数にデータを入れることにより、表示部分で .Resultと書かなくすることにより、無事解決しました。
追記
.NETラボでいつもBlazorなどの話をしてくださるくさばさんがこちらの問題について書かれているMicrosoft Learnのページを教えてくれました。まさにやってはいけないと書かれていました。
まとめ
シンプルではありますが、Blazor Server アプリが止まってしまい、困った時の解決策を紹介しました。
Blzorを最初使ったときは使いにくくて困ったのですが、今の時代は ChatGPTにも聞けますし、Copilotで枠を作ってから修正するなどもできるので、新しい言語や環境は試してみるのが一番と思いました。これによりいい感じで勤怠システムのサンプルが出来つつあるので、また進展があれば報告いたします。
Discussion