皆さん、こんにちは!
ソリューションコンサルタントの狩谷です。
この記事では、Blazor における DI の各 Scope について、Blazor Server、Blazor WebAssembly それぞれの観点で見てみようと思います。
- DI とは?
- DI の Scope とは?
- Blazor Server における各 Scope の動作について
- Blazor WebAssembly における各 Scope の動作について
- まとめ
- 技術サポート・無料オンライン相談会をご利用ください
DI とは?
DI とは Dependency Injection の略で、依存性注入と訳されます。クラスを疎結合にし、柔軟なソフトウェア開発を可能にします。 例えば、メール送信のためにサードパーティのライブラリがあるとします。通常、このライブラリを利用する場合は、ライブラリのクラスのインスタンスを作成して利用します。この方法ですと、ライブラリのクラスと使用するクラスが密結合になってしまいます。 DIをすると、インターフェイスを経由してクラスのインスタンスを取得できるため、クラス間を疎結合にできます。 DIの詳細については、ここでは述べませんので、他の記事や書籍等を参照してください。
DI の Scope とは?
DI によってクラスのインスタンスを柔軟に生成できますが、インスタンスを生成する際に、インタンスが有効な範囲を決められます。このインスタンスが有効な範囲が Scope になります。ASP.NET Core Blazor の標準の DI を利用する場合、Transient、Scoped、Singleton の 3 つの範囲でインスタンスを管理できます。それぞれ基本的な範囲は以下のようになりますが、Blazor Server か Blazor WebAssembly かによって動作に違いがあります。
範囲 | 説明 |
---|---|
Transient | コンポーネントにアクセスされるたびにインスタンスが生成されます。 |
Scoped | 利用ユーザー単位でインスタンスが生成されます。各コンポーネント間で共通のインスタンスが利用されます。 |
Singleton | アプリケーション全体でインスタンスが生成されます。そのため、異なるユーザー間でもインスタンスが共有されます。 |
Transient の動作については、Blazor Server、Blazor WebAssembly の違いはありませんが、Scoped、Singleton については動作に違いがあるため、それぞれ解説していきたいと思います。
Blazor Server における各 Scope の動作について
Blazor Server における各 Scope の動きを確認したいと思います。まずは、標準で生成される Counter コンポーネントのカウント数を保持するサービスを作成します。
public class CounterService { public int Value { get; set; } public void Increment() { Value++; } }
Transient の場合
作成した CounterSerive.cs を、Program.cs にて DI コンテナに登録します。 (.NET5 までの場合は、Startup.cs の public void ConfigureServices(IServiceCollection services) で登録してください。)
builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton<WeatherForecastService>(); builder.Services.AddTransient<CounterService>(); // 👈 追加 var app = builder.Build();
Counter.razor を修正します。
@page "/counter" @inject CounterService CounterService <PageTitle>Counter</PageTitle> <h1>Counter</h1> <p role="status">Current count: @CounterService.Value</p> <button class="btn btn-primary" @onclick="CounterService.Increment">Click me</button> @code { }
動作を確認してみます。コンポーネントの表示を切り替えると、インスタンスが初期化されるため、カウントが 0 に戻ります。
Scoped の場合
今度は、CounterSerive.cs を Scoped で DI コンテナに登録します。
builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton<WeatherForecastService>(); builder.Services.AddScoped<CounterService>(); // 👈 追加 var app = builder.Build();
コンポーネントを切り替えても、Counter のカウントが保持されています。ただし、リロードするとカウントが初期化されてしまいます。
Singleton の場合
最後に、CounterSerive.cs を Singleton でDIコンテナに登録します。
builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton<WeatherForecastService>(); builder.Services.AddSingleton<CounterService>(); // 👈 追加 var app = builder.Build();
コンポーネントを切り替えても、Counterのカウントが保持され、別のブラウザー(別のユーザー)で表示しても同じカウントが表示されます。リロードした場合も0に戻らず、最新のカウント数が表示されます。
Blazor Server における DI の Scope のまとめ
Blazor Server は名前の通り、Server 側でプロセスが実行されているため、Sigleton の場合は同一プロセス内でインスタンスが共有されます。そのため、複数のブラウザーや複数のユーザーでアクセスした場合に、同じインスタンスが使用されます。Scoped の場合は、クライアントとサーバー間の SignalR 接続ごとにインスタンスが作成されます。そのため、接続が有効な限り同一のインスタンスが使用されます。ブラウザをリロードすると、再接続されるため、新しくインスタンスが生成されます。 図にしたものが以下のものになります。
Blazor WebAssembly における各 Scope の動作について
続いて、Blazor WebAssembly における各 Scope の動きを確認したいと思います。同様に Counter コンポーネントのカウント数を保持するサービスを使用します。
Transient の場合
動作を確認してみます。Blazor Server の場合と同様に、コンポーネントの表示を切り替えると、インスタンスが初期化されるため、カウントが 0 に戻ります。
Scoped、Singleton の場合
Blazor WebAssembly の場合は、Scoped と Singleton の動作は同じ動作になります。これは、ブラウザのタブごとに WebAssembly のプロセスが実行されるため、Singleton のプロセスが有効な範囲と Scoped の範囲が同じ範囲になるためです。そのため、Singleton の場合でも、リロードすればインスタンスが再生成されます。タブが違えば別プロセスとなるため、別インスタンスとなります。
Blazor WebAssembly における DI の Scope のまとめ
Blazor WebAssembly では、各タブは固有のプロセスとなるので、Scoped、Singleton でも同様の動きとなります。 ブラウザの各タブはそれぞれ独立したアプリケーションプロセスであり、共通の状態を共有しません。 図にしたものが以下のものになります。
まとめ
Blazor Server、Blazor WebAssembly それぞれにおいて、プロセスの実行環境が異なります。それぞれの環境の特徴を理解し、効果的に DI を活用できればと思います。
技術サポート・無料オンライン相談会をご利用ください
インフラジスティックスのUI製品は多くの機能を備えているためドキュメントの情報量も多く、なかなかお探しの情報に辿り着けない場合もあります。そういった際はお気軽に技術サポートや、製品導入支援担当との無料オンライン相談会をご予約いただくことで検証時間を節約可能ですので、ぜひご活用ください。
技術サポートへの問い合わせ方法を確認する
無料オンライン相談会を予約する