====== Blazor ====== ===== 便利ツール ===== テーブル作成\\ * [[https://divtable.com/generator/|DIV TABLE生成サイト]] * [[https://tabletag.net/ja/|TABLEタグ生成サイト]] ===== Tips ===== ==== 初回アクセスページ ==== === Blazor Server === Pages フォルダにある「_Host.cshtml」ファイルとなる。 === Blazor WebAssembly === wwwroot フォルダにある「index.html」ファイルとなる。 ==== UIフレームワーク ==== >MatBlazor が一番有名な UI フレームワークなのですが、GitHub のスターがより多い「Ant Design Blazor」となる。 >[[https://blazor-master.com/blazor-ui-framework/|【無料】Blazor 対応の「使える」UI フレームワーク5選]] https://ant.design/components/overview/ === AntDesign 採用例 === * [[https://zenn.dev/okazuki/articles/and-blazor-lab|Blazor で HTML と CSS で画面作りたくないよぉ… AntDesign 編]] * [[https://mebee.info/2022/02/02/post-55944/|ASP.NET core 6 Blazor Server AntDesignを使用する]] === BlazorStrap 採用例 === == Blazor Server Side へのインストール方法 == * リリース版 https://blazorstrap.io/VNext/V5 * 最新プレビュー版 https://github.com/chanan/BlazorStrap/releases - nuget から BlazorStrap と BlazorStrap.V5 の2つのパッケージをダウンロードします。 - _host.cshtml を次のように変更します。 -
追加されていることを確認 (YourAssemblyName)の部分は書き換える - - - 追加 の最後に - -
NuGetのパッケージキャッシュフォルダは、デフォルトは「%userprofile%\.nuget\packages」
* [[https://devadjust.exblog.jp/28159915/|"Razor Class Library" パッケージについて]]
* [[https://forum.radzen.com/t/where-can-i-find-the-content-folder/4991|_content フォルダーはどこにありますか]]
==== スタートアップページの設定 ====
Blazor アプリケーションのスタートアップ ページは Index.razor です。\\
これを Login.razor に変更したい場合、下記のようにします。
- Index.razorから「@page "/"」 を削除します。
- Login.razor に「@page "/"」 を追加します。
[[https://stackoverflow.com/questions/56876631/setting-a-startup-page-in-blazor|Setting a startup page in Blazor - stackoverflow]]
==== アイコン使用 ====
=== Bootstrap Icons ===
1,800以上のアイコンが使用できる。\\
https://icons.getbootstrap.com/
本家からはnpmのみで、NuGetによるインストールは用意されていない。
[[https://github.com/windperson/BlazorBootstrapIconsdDemo|Using Bootstrap Icons in ASP.NET Core Blazor project]]
== 任意のサイズ ==
font-size と color を使用して、アイコンの外観を変更\\
https://bootstrap-guide.com/extend/icons/bootstrap-icons
=== FontAwesome ===
Web系の開発でアイコン表示などによく使われている。
現時点で、Font Awesome 6が最新である。
=== Open Iconic ===
.NET 7までは、「Open Iconic v1.1.1」のアイコン223種類が使用できる。.NET 8から除外された。\\
https://www.nsbasic.com/app/OpenIconic.html
>Open Iconicは223ものアイコンをOSSとして公開されているアイコンフォントです。EOTやWOFF、OTFだけでなく、PNG、SVGなどのフォーマットも用意されています。また、マルチカラー化や用意された複数サイズのフォントを利用してRWDに対応する、等も可能のようです。シンプルで汎用性の高そうなデザインですね。ライセンスはMIT、フォントはOFLとして公開されています。
>[[http://kachibito.net/useful-resource/open-iconic|オープンソースとして公開されているアイコンフォントのセット・「Open Iconic」]]
ナビゲーションのホームアイコン
==== Razorコンポーネントのライフサイクル ====
[[https://blazor-master.com/component-lifecycle/|【Blazor】Razorコンポーネントのライフサイクルを解説する]]
実行の順番
^メソッド名^呼び出し^補足^
|SetParametersAsync|パラメーターが設定されるタイミング| |
|OnInitialized(Async)|コンポーネントが初期化されるタイミング| |
|OnParametersSet(Async)|コンポーネントが初期化されるタイミングと、受け取るパラメータが更新されたタイミング|
|OnAfterRender(Async)|コンポーネントがレンダリングされたあと|初回実行のときは、引数の firstRender が true となる|
|ShouldRender|コンポーネントがレンダリングされるたび|true を返すとレンダリングを続行する|
|StateHasChanged|レンダリングしたい任意のタイミング|コストが高いので不必要に呼び出さないこと|
=== OnParametersSet(Async)について ===
Navigation.NavigateToで別ページに画面遷移させた時でもOnParametersSetAsyncが動作する。
Navigation.NavigateToで別ページに画面遷移させた時に例外エラー、原因として OnParametersSetAsync処理内で発生していた。
NavigateTo前に_isNextPageフラグをTrueにセットして使用して回避させた。
protected override async Task OnParametersSetAsync()
{
// 別ページに画面遷移する場合は何もしない。
if (_isNextPage) return;
==== Razorコンポーネントのステート ====
[[https://www.misuzilla.org/Blog/2020/12/25/BlazorComponentLifecycle|Blazor のコンポーネントのステートについて]]
ステートの変更通知はどのようなタイミングで発生するのかですが、おおまかに下記の3パターンで発生します。
- StateHasChanged メソッドを明示的に呼び出したとき
- イベント発火時 (@bind や @onclick とか)
- Parameter が変わったとき
2つ目と3つ目は暗黙的に StateHasChanged を呼び出している。\\
StateHasChanged はステートが変更されたことを通知するメソッドです。このメソッドを呼び出すと再レンダリング候補としてマークされます。
==== コードビハインド ====
razorファイルの中にHTML要素とバインドするC#コードを合わせた記載はやめて、razorコンポーネントとC#コードを分離して記載する。\\
[[https://qiita.com/nobu17/items/b7dc78db7beb1d833dc8|Blazorでコードビハインドでロジックとビューを分離して記述する]]
ロジックとビューを分離することで、下記のようなメリットなどが得られます。
* ロジックに対するUnitTestの記載が可能
* 基底のコンポーネントクラスの作成などによる共通処理の作成
=== 方法 ===
raozrファイルと対になるようにcsのファイルを作成するだけです。\\
Counter.razor
Counter.razor.cs(追加)
コード側のクラスには、partialキーワードを付けます。\\
例 public partial class Counter
==== 最大長(maxlength)を設定する方法 ====
BlazorのInputコンポーネントに maxLength が見つからない。
>これらの組み込みコンポーネントは、レンダリングされた出力に HTML 属性を渡します。したがって、例で行ったのとまったく同じように、HTML 属性を指定するだけでかまいません
>[[https://stackoverflow.com/questions/67511973/how-to-set-textarea-input-max-length-in-blazor|How to set textarea/input max length in Blazor - stackoverflow]]
組み込み専用の属性がないだけで、HTML 属性はそのまま指定すれば機能する。
==== ボタンにフォーカスを設定する方法 ====
=== UIフレームワーク未使用 ===
有名なUIフレームワークでも、Buttonのフォーカスが用意されていないことがあるので、その場合に標準機能で代替する。
@code {
public ElementReference? ControlButton { get; set; } = default!;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (ControlButton != null)
await ControlButton.Value.FocusAsync();
}
}
=== BlazorStrap使用 ===
※BlazorStrap/5.2.100-Preview3b が必要\\
https://github.com/chanan/BlazorStrap/issues/599
Click me
@code {
public ElementReference? ControlButton { get; set; } = default!;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (ControlButton != null && ControlButton.Element != null)
await ControlButton.Element.Value.FocusAsync();
}
}
==== BSInputGroupを中央寄せにする方法 ====
BSInputGroupでdisplay:flex となっているため、style属性で"display:block に書き換えてから中央寄せ(text-align: center)とする。
バーコード
手動選択
==== Boostrap 5による変更点 ====
bootstrap5では中東の言語で主流である右書きに対応したことからleft→start、right→endという概念の変更が導入されました。\\
これにより、ml-〇 → ms-〇にmr-〇 → me-〇 というようにクラス名が変更になりました。
[[https://nao-tokyo.jp/%E6%8A%80%E8%A1%93%E6%83%85%E5%A0%B1/changing-name-of-class-at-bootstrap5/|【bootstrap5】classのml、plとmr、prの名称が変更]]
====HTMLタグを出力する ====
MarkupStringオブジェクトにキャストすることでタグの内容を出力できます。
@((MarkupString)messageText)
@code {
string messageText;
void ButtonClick()
{
messageText = "見出しです
My Button Clicked
";
}
}
* [[https://www.ipentec.com/document/csharp-blazor-application-output-html-tag|BlazorアプリケーションでHTMLタグを出力する]]
* [[https://stackoverflow.com/questions/50604366/is-there-an-equivalent-to-html-raw-in-blazor|Is there an equivalent to Html.Raw in Blazor? - stackoverflow]]
==== 最上位のCSSを定義 ====
フォントサイズなど、bootstrap.css で定義されたサイズが優先されてしまう場合、important.css にてCSSを定義することにより優先させるようにする。
画面サイズによりフォントサイズを変更する。
.font-adjust {
font-size: 16px !important;
}
@media (min-width: 760px) {
.font-adjust {
font-size: calc(16px + ((1vw - 7.6px)*(16/4.4))) !important;
}
}
@media (min-width: 1200px) {
.font-adjust {
font-size: 32px !important;
}
}
* [[https://style.potepan.com/articles/22667.html#i-2|【HTML】フォントサイズの指定方法・単位・変わらない時の対処法まとめ]]
==== テキストボックスのEnterキーでサブミットさせる ====
IsSubmit="true" を付けることで、Enterキーでサブミットさせることが出来る。
==== bindとonchangeの併用は不可 ====
onchange を追加すると、Model.Procedure に値がセットされなくなる。
[[https://ryuichi111std.hatenablog.com/entry/2019/05/12/132636|bindとonchangeの併用は不可]]
=== 対応 ===
onchange イベント処理内で値をセットする。
private void OnProcejureChange(ChangeEventArgs e)
{
Model.Procedure = e.Value.ToString() ?? "";
}
==== MainLayout コンポーネント側にデータを反映させる ====
カスケーディングを使用してパラメーターを渡す。
>カスケードとは、もともと「連なった小さな滝」という意味があります。
>Blazor では滝が流れるイメージで、上位の階層から下位の階層にパラメーターを渡すことができる仕組みがあります。
>これが「カスケーディングパラメーター」です。
* [[https://blazor-master.com/cascading-parameter/|【Blazor】カスケーディングを使ってパラメーターを渡す方法]]
* [[https://www.appsloveworld.com/blazor/100/2/is-there-any-way-to-communicate-to-main-layout-of-blazor-pages|[SOLVED]-IS THERE ANY WAY TO COMMUNICATE TO MAIN LAYOUT OF BLAZOR PAGES-BLAZOR]]
==== クライアントのIPアドレスの取得 ====
BlazorServerではサーバーで動作しているので、クライアントのIPアドレスを取得するにはクライアントから情報を取得する。\\
MainLayoutの子コンポーネント側に表示させるため、カスケーディングを使用してパラメーターを渡す方式にした。
[[https://qiita.com/takaOG/items/375d67397ddc3dc77fc9|BlazorServerでクライアントのIPアドレスを全ページで共有する]]
==== クエリパラメーターのセットと取得 ====
=== 単独変数 ===
var parameter = new Dictionary
{
{ "CPCode", value },
{ "TermID", Layout.Info.IPAddress }
};
var newuri = Navigation.GetUriWithQueryParameters("./Machine", parameter);
Navigation.NavigateTo(newuri, false);
[Parameter, SupplyParameterFromQuery]
public string? CPCode { get; set; }
[Parameter, SupplyParameterFromQuery]
public string? TermID { get; set; }
=== 複数変数 ===
var readOnlyDictionary = new Dictionary
{
["film"] = 1,
["film"] = 2,
};
var uri = Navigation.GetUriWithQueryParameters(readOnlyDictionary);
// http://localhost:5000/MovieComparer?film=1&film=2&film=3
[Parameter, SupplyParameterFromQuery(Name = "film")]
public string[]? Films { get; set; }
=== 通常クラス ===
Blazorコンポーネント内では Navigation.GetUriWithQueryParametersを使用できるが、Blazorコンポーネント外の一般クラスでは、HttpUtility.ParseQueryString を使用する。
[[https://ohke.hateblo.jp/entry/2018/04/01/230000|C# クエリストリング(?var=hoge&...)を作る]]
=== Uriからパラメータ取得 ===
NameValueCollection query = HttpUtility.ParseQueryString(new Uri(Navigation.Uri).Query);
string key = query["Key"] ?? ""
[[https://jasonwatmore.com/post/2020/08/09/blazor-webassembly-get-query-string-parameters-with-navigation-manager|Blazor WebAssembly - Get Query String Parameters with Navigation Manager]]
=== 参照 ===
* [[https://www.variablenotfound.com/2021/11/generacion-de-rutas-con-parametros-de.html|Generación de rutas con parámetros de query string en Blazor 6]]
* [[https://jonhilton.net/blazor-deep-linking/|Use the Query String to pass an array of selected values between Blazor Pages]]
==== セッションの扱い(状態管理) ====
[[https://learn.microsoft.com/ja-jp/aspnet/core/blazor/state-management?view=aspnetcore-7.0&pivots=server|ASP.NET Core Blazor 状態管理]]
=== コンポーネント内でデータを保存し、読み込む ===
ブラウザー ストレージのデータの読み込みまたは保存が必要なすべてのコンポーネントで、@inject ディレクティブを使用して、次のいずれかのインスタンスを挿入します。
* ProtectedSessionStorage
* ProtectedLocalStorage
[[https://zenn.dev/yoshi1220/articles/8f4783ade9c06d|BlazorでWeb Storageを利用して一時的にデータを保存する方法]]
=== メモリ内状態コンテナー サービス ===
入れ子になったコンポーネントと入れ子になっていないコンポーネントでは、登録済みのメモリ内状態コンテナーを使用してデータへのアクセスを共有できます。
== Scopeについて ==
^メソッド^内容^
|AddTransient()|インジェクション毎にインスタンスを生成|
|AddScoped()|リクエスト毎にインスタンスを生成|
|AddSingleton()|アプリケーション内で1つのインスタンスを生成|
builder.Services.AddScoped();
* [[https://zenn.dev/yoshi1220/articles/22b99b1e3717e3|BlazorにおけるDIのScopeについて]]
* [[https://morioh.com/p/3c50299e2c4d|.netコアのAddScopedとAddTransientとAddSingletonの違い]]
==== injectの記載方法 ====
ディレクティブ記法でオブジェクトをインジェクトします。
@inject 【インジェクトするオブジェクトの型】 【インジェクトする変数名】
@inject StateService Service
上記の書き方以外にコードブックにて[Inject]属性を付与する記法があります。
今後コードビハインドで記述するならこっちの方が分かりやすいです。
[Inject]
private StateService Service { get; set; } = default!;
==== 物理パスの取得 ====
Blazor Serverの場合、Directory.GetCurrentDirectory() を使用すればいい。
string rootpath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "wwwroot");
* [[https://stackoverflow.com/questions/68183250/how-can-i-find-the-physical-path-of-wwwroot-in-a-blazor-app-razor-page-without|How can I find the physical path of wwwroot, in a Blazor app razor page without a controller?]]
* [[https://www.c-sharpcorner.com/article/how-to-create-pdf-using-itextsharp-in-blazor/|How To Create PDF Using iTextSharp In Blazor]]
==== Web APIを追加 ====
Blazor ServerにWeb APIを追加する。
サンプル
- Controllerフォルダを作成
- 右クリックメニューから[追加]の[コントローラー]をクリックする
- 共通のAPIにある「読み取り/書き込みアクションがある API コントローラー」を選択して[追加]ボタンをクリックする
- ValuesController.cs を追加する
下記コードをapp.Run()より前に、該当するところに追加する。
// Add MVC Controllers
builder.Services.AddControllers();
app.MapRazorPages();
app.MapControllers();
Urlにてapiの後にコントローラー名を入れてアクセスすれば、値が返ってくる。
https://localhost:7072/api/Values
=== 参照 ===
* [[https://chanmingman.wordpress.com/2020/07/12/add-web-api-controller-to-blazor-project-asp-net/|Add Web API controller to Blazor project ASP.NET]]
* [[https://stackoverflow.com/questions/75242436/how-to-add-controller-endpoints-to-razor-server-in-asp-net-core-7-0-vs2022|How to add controller endpoints to Razor Server in ASP.NET Core 7.0 / VS2022 - stackoverflow]]
* [[https://stackoverflow.com/questions/74691624/net-7-and-useendpoints|.NET 7 and UseEndPoints() - stackoverflow]]
==== HttpClientの使用 ====
「@Inject HttpClient httpClient」を使用すると下記エラーが発生する。
>Unhandled Promise Rejection: Error: System.InvalidOperationException: Cannot provide a value for property 'Http' on type . There is no registered service of type 'System.Net.Http.HttpClient'.
* [[https://stackoverflow.com/questions/55220269/server-side-blazor-does-not-provide-httpclient-for-injection|Server-side Blazor does not provide HttpClient for injection - stackoverflow]]
Injectを使用しないで、new HttpClient() とする。
==== ボタンクリックイベントでSUBMIT処理 ====
ボタンクリックイベントでは、F1キーを入力させたようにしたいため、IsSubmit(type="submit")を使わないで制御したい。
↓
メニューボタンをクリックしたら、入力値にF1キーをセットしてSUBMIT処理を呼ぶ。
public void OnMenuClick()
{
MatchViewModel Model = new();
Model.Input1 = Const.INPUT_KEY_F1;
EditContext e = new(Model);
OnSubmit(e);
}
==== カスタムコンポーネントのbind-value ====
bind-value を指定したたけでは、"does not have a property matching the name 'ValueChanged'"エラーとなり動作しない。\\
そのため、ParameterにValueChangedを定義する。
@code {
public string InputValue = "Example";
}
Welcome to Blazor Application, @Value
@code {
[Parameter]
public string? Value { get; set; }
[Parameter]
public EventCallback ValueChanged { get; set; }
private async Task OnInputChange(ChangeEventArgs args )
{
Value = (string?)args.Value;
await ValueChanged.InvokeAsync(Value);
}
}
[[https://www.syncfusion.com/faq/blazor/components/how-do-you-use-bind-value-and-bind-valueevent-on-a-custom-component|How do you use bind-value and bind-value:event on a custom component?]]
==== カスタムコンポーネントで親コンポーネントの処理を実行 ====
カスタムコンポーネントのOnClickCallbackパラメータに親側の実行処理を記載する。\\
カスタムコンポーネント側のボタンをクリックすると親側の処理が実行される。
// イベント処理をラムダ式で書いた場合
await OnClickCallback.InvokeAsync()>
@code {
[Parameter]
public EventCallback OnClickCallback { get; set; }
}
[[https://blazor-master.com/blazor-event-callback/|【Blazor】EventCallbackで子コンポーネントからイベントを受け取る方法]]
==== 子コンポーネントのメソッドを実行 ====
親コンポーネント内で子コンポーネントを@refを用いて参照することでメソッドを呼ぶことが出来る。
[[https://lightningblog.net/blazor/basic-ref|【Blazor】 @refを使い方]]
==== リトライ回数を増やす ====
リトライ回数はデフォルトで8回となっている。
[[https://qiita.com/jsakamoto/items/2cdbe0764df262e9fd23|Blazor Server でサーバーとの接続障害からの復旧後、「再読込」をユーザに押させることなく自動でページ再読み込みする]]
==== awaitのメソッドを追加すると動作しなくなる ====
Submitの先頭にawait.Task.Delay メソッドを実行すると、エラー用のメッセージモーダルが表示されなくなる現象があった。\\
async void を async Task に変更することで解決した。
async void Submit()
↓
async Task Submit()
[[https://qiita.com/tokishirazu/items/12c581f0ad4a6bcd562b|C#の非同期メソッドの戻り値のvoidとかTaskがよくわからなかったので動かしてみた]]
==== Javascriptとの連携 ====
* [[https://qiita.com/Cable08/items/6d81e1cf2df9067123cf|【Blazor】JavascriptからC#(.Net)のstaticもしくはインスタンスメソッドを呼び出す。]]
* [[https://stackoverflow.com/questions/66385844/call-a-c-sharp-non-static-method-from-a-static-method-in-blazor-invoked-by-javas|Call a C# non static method from a static method in Blazor invoked by Javascript DotNet.invokeMethodAsync - stackoverflow]]
* [[https://imaginet.com/2021/guide-blazor-javascript-interop/|Guide to Blazor JavaScript Interop]]
==== BlazorとjQueryコンポーネント等との連携 ====
jQueryコンポーネント または jQueryを使用しないJavaScriptコンポーネントをBlazorと連携する場合の注意点
* [[https://gavilan.blog/2020/05/18/using-events-with-javascript-and-jquery-in-blazor-initializing-libraries/|Blazor での JavaScript および jQuery でのイベントの使用 – ライブラリの初期化]]
* [[https://thesassway.com/how-to-use-jquery-in-blazor/|How to Use jQuery in Blazor]]
* [[https://stackoverflow.com/questions/54274629/how-to-use-jquery-ui-from-blazor-component|How to use jQuery UI from Blazor component - stackoverflow]]
* [[https://www.codeproject.com/Tips/5290232/Blazor-datepicker-Component-using-JQuery|Blazor datepicker Component using JQuery]]
* [[https://github.com/dotnet/aspnetcore/issues/13732|understand the onchange listener to trigger the setter on javascript
#13732]]
* [[https://stackoverflow.com/questions/58777642/blazor-fire-onchange-event-when-chosen-drop-down-value-changes|Blazor fire onchange event when Chosen drop down value changes - stackoverflow]]
下記のようにすれば、onchangeイベントを呼ぶことが出来る。この方法だと DotNet.invokeMethodAsync を使わずに済む。
$("#justAnotherInputBox").on('change', function () {
var myElement = $(this)[0];
var event = new Event('change');
myElement.dispatchEvent(event);
console.log("Change");
});
private void OnChange(ChangeEventArgs e)
{
if (_comboTree != null || e.Value != null)
{
Console.WriteLine(e.Value);
}
}
=== @ref 属性による弊害 ===
inputタグに @ref 属性を付けて、 @ref="CurrentElement" として、JQuery Pluginである ComboTree に渡していた。これで問題なく表示して動作していたが、同じ画面を再度表示しようとした際に this._input.addClass や this._input.wrap で例外エラーが発生してしまう。どうも追加するinputタグが見つけれない状態
_comboTree = await _jsClass.InvokeAsync("createComboTree", CurrentElement, options);
createComboTree(element, Options) {
this.combo = $(element).comboTree(Options);
@ref 属性ではなく、一般的なid属性の使用に切り替えることで例外エラーが発生しなくなった。
_comboTree = await _jsClass.InvokeAsync("createComboTree", "#" + ElementId, options);
=== nullを変換できない場合 ===
Datetimepicker(tempusdominus)のv6を使用した際に、範囲最小値(minDate)と範囲最大値(maxDate)の指定でminDate側にnullをセットして実行した段階で、nullに変換できないとしてエラーとなった。
null 値のプロパティを無視する属性を付けることで回避できた。\\
[[https://qiita.com/lx-sasabo/items/e3a5dae6974e565a1823|System.Text.Json で null 値のプロパティを無視する]]
private class Restrictions
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public DateTime? minDate { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public DateTime? maxDate { get; set; }
}
==== ログ出力にlog4netを使用する ====
[[https://code-maze.com/aspnetcore-structured-logging-log4net/|Structured Logging in ASP.NET Core With log4net]]
==== デバッグ詳細 ====
an unhandled exception on the current circuit のエラーの場合、詳細エラーを出力
https://stackoverflow.com/questions/57514541/how-to-turn-on-circuitoptions-detailederrors
{
"DetailedErrors": true, // turns on CircuitOptions.DetailedErrors
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug" // turns on SignalR debugging
}
},
"AllowedHosts": "*"
}
==== ブラウザのキャッシュ回避 ====
Blazorには、ASP.NET Coreにあった「asp-append-version="true"」 が見当たらない。\\
[[https://pg-himajin.com/dotnet/razor-pages/browser-cache-update/|Razor Pagesでファイル更新時にブラウザキャッシュを更新する方法]]
よって、ファイルバージョンを付けて回避している。
@code {
private string Version = "v=" + FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion + "";
}
==== WebSocketプロトコルを使用する ====
[サーバーの役割] ページで、[Web サーバー (IIS)] を展開し、[Web サーバー]、[アプリケーション開発] の順に展開し、[WebSocket プロトコル] を選択して、インストールする。
>Blazor Server は、WebSocket では通信できない環境であっても、Long Polling 方式に動作を切り替えて、動作継続します。
>[[https://qiita.com/jsakamoto/items/4f7bfbbb3e3ba2f31614|WebSocket で通信できない環境では Blazor Server は動作しないのか?]]