DIコンテナを理解するためは、IServiceCollection オブジェクト についての理解が不可欠です。
今回は、まず、IServiceCollection オブジェクト について記述します。
次に、IServiceCollection オブジェクト の拡張メソッドについて記述します。
最後に Razor Pages と Entity Framework Core(EF Core) の各フレームワークにおけるDIコンテナの管理について記載します。
IServiceCollection オブジェクト とは、IServiceCollection インターフェースを実装したクラスのオブジェクトです。
IServiceCollection オブジェクト は、アプリケーションで使用されるサービスのコレクションを管理するオブジェクトです。
サービスのコレクションとは、DIコンテナに登録されたサービス群のことです。
DIコンテナに登録するサービスの具体的な形態は、インターフェースの実装クラス、抽象クラスの実装クラス、デリゲート、ラムダ式などのオブジェクトです。
IServiceCollection オブジェクト は、DIコンテナにこれらのサービスを登録したり、登録したサービスをインスタンス化して取得・注入したりするメソッドを持っています。
これらのメソッドをもちいて、DIコンテナに登録されたサービス群を管理(登録、インスタンス化、注入など)します。
前回紹介したDIコンテナにサービスを追加する次の3つのメソッドも IServiceCollection オブジェクトのメソッドです。
・AddSingleton : アプリケーション全体で1つのインスタンスが作成されます。
・AddTransient : サービスが必要なたびに新しいインスタンスが作成されます。
・AddScoped : 同じスコープ内で1つのインスタンスが作成されます。
つぎに、IServiceCollection オブジェクト を取得するまでの手続きについてコード例を参考にして確認していきます。
アプリケーションが使用するサービスをDIコンテナに登録する処理は、通常、エントランスになるProgram.csの中で行います。
Program.csクラスのサービスの登録に関するコードを抜粋したものを下記に示します。(青字の部分)
using ContosoUniversity.Data; using Microsoft.EntityFrameworkCore; var builder= WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddDbContext<SchoolContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString(“SchoolContext”))); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); var app = builder.Build();; |
IServiceCollection オブジェクト を取得するまでの流れは次のようになります。
① var builder = WebApplication.CreateBuilder(args);
WebApplication.CreateBuilder(args) で WebApplicationBuilder クラスの新しいインスタンスを生成し、インスタンスをbuilder変数に代入します。
② builder.Services.AddRazorPages();
WebApplicationBuilderのオブジェクトのServicesプロパティは IServiceCollection オブジェクト の参照を保持していますので、
IServiceCollection オブジェク トの AddRazorPages メソッド で Razorページをサポートするために必要なサービスとして RazorPagesServices クラス と DefaultPageModelActivator クラス をDIコンテナに登録しています。
③ builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString(“SchoolContext”)));
このコードはSchoolContext.csをDIコンテナに登録しています。
抜粋したコードの青字の部分をクラスの相関関係が分かるようクラス図にしました。
WebApplication |
CreateBuilder(args) |
↓(WebApplicationBuilderインスタンス生成)
WebApplicationBuilder |
Servicesプロパティ |
↓(ServicesプロパティがIServiceCollectionの参照を保持)
IServiceCollection |
サービス管理の各種メソッド |
↓登録 ↑取得
DIコンテナ |
サービス |
このようにして取得した IServiceCollection オブジェクト のメソッドをもちいてDIコンテナにサービスを登録したり、インスタンス化と注入を行います。
これらのメソッドは、拡張メソッド という種類のメソッドです。
IServiceCollection には数多くの 拡張メソッド が登録されています。
拡張メソッド とは、静的なメソッドを IServiceCollection オブジェクト に登録しておくことで、IServiceCollection インターフェース型で使うことのできる、メソッドです。
つまり、静的なクラス(インスタンス化しないで使用できるクラス)で作成したメソッドが登録されています。
また、プログラマーが 拡張メソッド を作成して登録することもできます。
先ほどの、AddSingleton、AddTransient、AddScopedも 拡張メソッド です。
Program.csのコード例でいうと、AddRazorPages と AddDbContext が 拡張メソッド です。
builder.Services.AddRazorPages(); |
builder.Services.AddDbContext<SchoolContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString(“SchoolContext”))); |
AddRazorPages と AddDbContext の 拡張メソッド は、Razor Pages や EF Core がインストールされると、自動的に、 IServiceCollection オブジェクト に登録されます。
続いて、Razor Pages と EF Core におけるDIコンテナのサービスの利用、つまりインスタンス化と注入についてです。
DIコンテナに登録した Razor Pages のサービスの利用は、Razor Pagesフレームワーク そのものが行うため、プログラマーが利用のためのコードを記述することはありません。
サービスの内容は、Razorページ(Index.cshtmlなど)や Razorページモデル(Index.cshtml.csなど) をインスタンス化して利用できるようにするもので、 Razor Pagesフレームワーク そのものが利用します。
EF Core は、次のようにしてインスタンス化と注入を行います。(青字の部分)
public MyController(SchoolContext context) とコンストラクターの引数に「SchoolContext context」を指定することで、DIコンテナがSchoolContextインスタンスを生成し、そのインスタンスをcontext変数に注入します。
このようにコンストラクターの引数で注入を行うことを コンストラクタインジェクション といいます。
このcontextの値(SchoolContextインスタンス)をプライベート変数の_context に代入し、_contextを使ってデータベースの操作を行います。
public class MyController : Controller { private readonly SchoolContext _context; public MyController(SchoolContext context) { _context = context; } // Use _context to interact with the database } |
以上、DIコンテナを理解するうえで必要とな るIServiceCollection インターフェース 、拡張メソッド について記述し、次に Razor Pages と EF Core のそれぞれのフレームワークがどのようにDIコンテナを利用しているかについて記述しました。
現時点における私の理解到達点をまとめたものですが、まだまだ理解不足ですし、間違って理解している部分があると思います。
おいおい、理解が深まっていくことを期待しましょう。
徒然に思うこと
武田真一アナウンサーがNHKを退職してフリーになったときの心境をあるインタビューで次のように話していました。
「いざ一歩を踏み出すと決めたとたん、一気に心が晴れました。不思議ですよね。5年後の自分が見えないという点では、何ら変わらないのに。先が見えないことを不安と捉えるのではなく、未知なるものへのワクワク感に置き換わったのです。」
「先が見えないことを不安と捉えるのではなく、未知なるものへのワクワク感に置き換わった」というくだり、私の琴線に触れて、共鳴しました。
私のサラリーマン人生と60才定年後の方向転換のとき、まったく同じ感覚を持った覚えがあります。