では、なぜBlazorがここでWeatherForecastServiceを呼び出せるのかという問題があります。
Program.csを開くと、次の行が見つかります。
builder.Services.AddSingleton<WeatherForecastService>();
このコードをコメントアウトし、ウェブページをリロードしてFetch dataメニューをクリックすると、ページの下部に次のような例外警告ブロックが表示されます(フッターに警告ブロックが1つだけ表示されます)。詳細な警告はターミナルの出力を確認してください。これは、FetchData.razorでWeatherForecastServiceを呼び出そうとしているのに、このサービスを登録することをBlazorに伝えていないからです。


警告の内容をコピーして確認すると、非常に明確です。
Cannot provide a value for property 'ForecastService' on type 'BlazorServer.Pages.FetchData'. There is no registered service of type 'BlazorServer.Data.WeatherForecastService'.
ただし、これはday03で説明した依存性注入とは異なります。依存性注入の目的は、高レベルのプログラムが低レベルのプログラムに依存せざるを得ない状況を解消し、結合度を下げることです。例えば、今日FetchData.razorが他のサービス、例えばNewWeatherForecastServiceの同名メソッドGetForecastAsyncを呼び出して10件のデータを取得する必要がある場合、WeatherForecastServiceを使用しているすべての箇所を修正しなければなりません。現在はデモの関係で数が少ないため影響は感じませんが、将来的に10~20箇所以上を修正する必要がある場合はどうでしょうか?
ここで依存性注入が活躍します。まずインターフェースを定義します。interface IWeatherForecastService
namespace BlazorServer.Data;
public interface IWeatherForecastService
{
Task<WeatherForecast[]> GetForecastAsync(DateTime startDate);
}
中に必要なメソッドを記述します。Task<WeatherForecast[]> GetForecastAsync(DateTime startDate);
実装する必要はありません(インターフェースにも実装を記述できます。C# 8.0ではインターフェースにデフォルト実装を指定する新機能が導入され、既存の実装を拡張しやすくなり、AndroidやSwiftのAPIとの相互運用も可能になりました)。次に、WeatherForecastServiceとNewWeatherForecastServiceにIWeatherForecastServiceを継承させます。

Program.csでは、IWeatherForecastServiceとNewWeatherForecastServiceを使用して登録するように変更します。

上のスクリーンショットのように、FetchData.razorでもIWeatherForecastServiceを注入するように変更します。
ページをリロードすると、データ件数が10件になっているのが確認できます。

依存性注入の核心は「ある機能への依存性が注入によって行われ」、低レベルのプログラムを直接呼び出すのではなく、低レベルのプログラムのインターフェースを呼び出すことです。これにより、低レベルのプログラムが変更されても、呼び出し側のすべてのプログラムを修正する必要がなくなります。
注:筆者はこの記事を書き終えた後にライフサイクルを思い出しました。当初はgit rebaseを使って今回のコミットに戻ってDemoを追加しようと考えましたが、gitの「detached HEAD」のリスクがあるため、day07でライフサイクルを説明します。ご不便をおかけして申し訳ございません。
注:本記事のコードは.NET 6 + Visual Studio 2022でリファクタリングされています。原文のリンクからリファクタリング後のコードを比較して学習できます。ご一読いただきありがとうございます。原作者をサポートしてください。