Blazor02

BlazorでのWebアプリケーション開発手法2【C#】B09_自作Serviceの依存性注入をする方法

この章について

前回のレクチャーでは、確認・警告・入力受付のダイアログ表示をまとめて扱うMessageServiceクラス(IMessageServiceインターフェース)を自作しました。今回はこのMessageServiceを実際にHome.razorへ組み込み、これまでJavaScriptのalert・confirm・promptを直接呼び出していた箇所を、自作サービス経由の呼び出しに置き換えていきます。あわせて、自作したクラスやインターフェースを@injectで利用できるようにするための「依存性注入(DI)」の登録方法を学びます。

MessageServiceのおさらい

前回作成したMessageServiceクラスは、IJSRuntimeを内部で保持し、Alert・Confirm・Promptという3つのメソッドを提供するIMessageServiceインターフェースを実装したクラスです。

図1 前回作成したMessageServiceクラス(IMessageServiceインターフェースを実装)

Home.razorでJSからMessageServiceへ切り替える

まずHome.razorを開き、これまで使っていた@inject IJSRuntime JSの行を削除します。今回はJavaScriptを直接呼び出すのではなく、MessageServiceを経由してダイアログを表示するようにしたいためです。

図2 @inject IJSRuntime JSの行を削除したHome.razor

削除した代わりに、@injectで自作のIMessageServiceを指定します。

@page "/"
@using BlazorApp1.Components.Services
@rendermode InteractiveServer
@inject IMessageService MessageService
@inject NavigationManager NavigationManager

 

図3 @inject IMessageService MessageServiceを追加

自作サービスは自分でDI登録が必要

NavigationManagerやIJSRuntimeのようにBlazorが標準で用意しているサービスは@injectと書くだけで自動的に使えますが、IMessageServiceのように自分で作成したインターフェースやクラスの場合は、そうはいきません。「IMessageServiceをちょうだい」と@injectで指定しても、フレームワーク側は「では実際に何を渡せばよいのか」を知らないため、事前にどのクラスを渡すのかを明示的に登録しておく必要があります。

図4 MessageService.cs/IMessageService.csはServicesフォルダーに配置されている

Program.csでサービスを登録する

登録先はProgram.csです。builder.Services.AddRazorComponents()やAddInteractiveServerComponents()が並んでいる箇所と同じように、builder.Services.AddScopedというメソッドを使って登録します。

図5 builder.Services.AddScopedの入力(IntelliSenseによる候補表示)

山カッコ(ジェネリック型引数)に、まずIMessageServiceを指定します。

図6 型引数にIMessageServiceを指定(IntelliSense候補からも選択できる)

続けてカンマで区切り、実際の実装クラスであるMessageServiceを指定します。これで「IMessageServiceが要求されたときはMessageServiceのインスタンスを渡す」という定義が完成します。

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();
builder.Services.AddScoped<IMessageService, MessageService>();

 

図7 登録が完了したProgram.cs(エラーなし)

AddScopedとAddTransientの違い

ここで使ったAddScopedのほかに、AddTransientという指定方法もあります。AddScopedはブラウザを開いている間(1つのセッション中)は同じインスタンスを使い回してよい場合に使います。一方、呼び出すたびに毎回新しいインスタンスを生成したい場合はAddTransientを使います。今回のMessageServiceは、呼び出すたびに作り直す必要がない単純なサービスなので、AddScopedを選択しています。この2つに加えてAddSingletonという指定方法もあり、それぞれの使い分けの詳細は依存性注入のライフサイクルを扱う回で解説されます。

Home.razor側のJavaScript呼び出しを置き換える

Program.csでの登録が完了すると、@injectしたMessageServiceには先ほど定義した通りMessageServiceクラスのインスタンスが渡されるようになります。あとはHome.razor側で、これまでJS.InvokeVoidAsyncやJS.InvokeAsyncを使っていた箇所を、MessageServiceのメソッド呼び出しに置き換えていくだけです。試しにこの時点でHome.razorを見てみると、@inject IJSRuntime JSを削除したことで、JSという名前が見つからないというエラーが表示されています。

図8 JSフィールドを削除したため発生したCS0103エラー(’JS’という名前は存在しません)

このJS.InvokeAsync<bool>(“MessageBoxConfirm”, …)の部分をMessageService.Confirm(…)に、JS.InvokeVoidAsync(“MessageBoxAlert”, …)の部分をMessageService.Alert(…)に、それぞれ順番に置き換えていきます。

図9 Save()内のConfirm呼び出しを置き換えている途中の様子(残りのJS呼び出しでエラーが表示されている)

Save()メソッドとLogin()メソッドの中にあるJS呼び出しをすべて置き換えると、それぞれ次のようになります。

private string _areaName = "";
 
private async Task Save()
{
    bool messageResult = await MessageService.Confirm("保存しますか?");
    if (!messageResult)
    {
        return;
    }
 
    if (string.IsNullOrEmpty(_areaName))
    {
        await MessageService.Alert("エリア名称を入力してください");
        return;
    }
 
    //save処理
    await MessageService.Alert("Saveしました!!");
}
private async Task Login()
{
    var random = new Random();
    int code = random.Next(100000, 1000000);
 
    //メール送信...
    System.Diagnostics.Debug.WriteLine("code:" + code.ToString());
 
    string inputCode = await MessageService.Prompt(
        "メールを送信しました。認証コードを入力してください。");
 
    if (code.ToString() != inputCode)
    {
        await MessageService.Alert("認証失敗!!");
        return;
    }
 
    NavigationManager.NavigateTo("counter");
}

 

図10 すべて置き換え終わった状態でデバッグ実行(エラーなし)

動作確認

実行して保存ボタンを押すと確認ダイアログが表示され、OKを押すと保存完了のアラートが表示されます。

図11 保存ボタン押下時に表示される確認ダイアログ「保存しますか?」

同様にログインボタンを押すと、認証コードの入力を求める入力ダイアログが表示されます。Confirm・Alert・Promptのすべてが、MessageService経由でも問題なく動作していることが確認できます。

図12 ログインボタン押下時に表示される入力ダイアログ「メールを送信しました。認証コードを入力してください。」

まとめ

このようにサービスとしてまとめておくことで、”MessageBoxAlert”のような文字列をあちこちに直接書く必要がなくなり、基本的にみんなが同じMessageServiceを使うようになります。今回のポイントは依存性注入(DI)です。これまで何気なく使ってきた@injectですが、NavigationManagerやIJSRuntimeのようなBlazor標準のサービスとは異なり、自分で作成したクラスやインターフェースについては、Program.csのbuilder.Services.Addで自分自身が定義しておく必要があります。その際、Scoped・Transient・Singletonという3つの登録方法のうちどれを使うかも合わせて考える必要があり、実際にクラウド環境に接続した際の挙動を含めた使い分けについては、依存性注入のライフサイクルを扱う次の動画で解説されます。

図13 次回予告:依存性注入(DI)のライフサイクル(AddScoped/AddTransient/AddSingleton)

BlazorでのWebアプリケーション開発手法2【C#】

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る

A01_はじめに
B01_メッセージを出すレイアウトの作成
B02_pタグを使ったメッセージ表示
B03_ポップアップメッセージを表示する方法
B04_問い合わせメッセージを表示する方法
B05_ユーザーの入力を受けるメッセージ
B06_インラインJavaScript非推奨
B07_Javascriptを分離してアクセスする方法
B08_MessageServiceを作成する
B09_自作Serviceの依存性注入をする方法
C01_EditFormを使った入力検証
C02_ValidationSummary
C03_ValidationMessage
C04_入力コンポーネント_InputText
C05_InputNumber
C06_未入力を許容する方法
C07_InputNumberで対応している型
C08_InputDate
C09_InputSelect
C10_入力コンポーネント_InputCheckbox
C11_InputRadioとInputRadioGroup
C12_InputTextArea
C13_InputFile
C14_ファイルの上限を設定する
C15_フォーカスをあてる方法_標準編_ElementReference
C16_フォーカスをあてる方法_Razorコンポーネント編
D01_StringLength
D02_RegularExpression_正規表現
D03_EmailAddress
D04_Compare
D05_ViewModelに分ける方法
D06_依存性注入を利用したViewModelの生成
D07_CustomValidation1
D08_CustomValidation2
D09_プロパティレベルのCustomValidation
D10_ValidationContext
D11_処理をViewModelに移す書き方
D12_ViewModelに移せないロジック
D14_ShouldRender
D15_ViewModelに書かないほうがいいもの
D16_エラー箇所にフォーカスを当てる方法
E01_Azureにデプロイするための事前作業
E02_Azureにアップするアプリの作成
E03_Azureへの公開準備
E04_Azureへの公開
E05_データベースの作成
E06_データベースのデータを取得する
E07_データベースの値を画面に表示する
E08_Azureのデータベースを作成する
E09_Azureのデータベースにポータルサイトからログインする
E10_AzureのDBにテーブルを作成する
E11_BlazorアプリからAzureDBに接続する
E12_SQLデータベース料金の注意
E13_AzureSQLデータベースの無料オファーを受ける
F01_依存性注入のライフサイクル
F02_クラウドでのAddSingletonの動き

■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る