しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET Core 1.0 の Startup クラス定義の基本と Dependency Injection の話

ASP.NET Core では Startup クラスを用意して、その中でアプリケーションの設定を行うようになっています。OWIN でも存在していた Startup クラスですが、ASP.NET Core では大きく変化しています。

大きく変化しているのですが、それぞれのメソッドで役割が綺麗に分離されているので、結構簡単に理解できると思います。基本的な流れは以下の通りです。

  1. コンストラクタ
    • JSON / 環境変数などから設定を読み込む
  2. ConfigureServices メソッド
    • サービスクラスを DI コンテナに登録する
  3. Configure メソッド
    • ミドルウェアを登録、ログの設定などを行う

これだけでも理解できそうな感じですが、せっかくなのでもうちょっと深堀してみます。

コンストラクタ

コンストラクタでは IHostingEnvironment が渡されるので、それを使って CondigurationBuilder を組み立てていくのが基本的な処理となります。

IHostingEnvironment は名前の通りホスティング環境の情報が入っているので、Production / Staging / Development のそれぞれに向けた設定に変えることが出来ます。

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();

    Configuration = builder.Build();
}

ConfigurationBuilder では設定を追加した順番に上書きされていくので、書き順が非常に重要になります。デフォルトでは環境変数に設定したものが優先されるようになっています。

Azure Web Apps にデプロイした場合に、App Settings から追加したものが優先されるので便利です。

ConfigureServices メソッド

ここでは IServiceCollection に実行時必要なクラスやインスタンスを追加していきます。ASP.NET Core MVC を使う場合は最低限 AddMvc メソッドを呼び出しておく必要があります。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddMvc();
}

他にも Entity Framework Core を使う場合には AddDbContext を呼び出して、オプションで DB への接続設定を追加したりします。ここで設定した DbContext は Controller などのコンストラクタで受け取れます。

後で説明しますが、AddDbContext のデフォルトではリクエストスコープでインスタンスが作られるようになっているので、安心して使えます。

Configure メソッド

これまでは Web.config などで行っていた設定を ASP.NET Core では Configure メソッドで行います。ミドルウェアは HttpModule みたいなものなので、案外すんなり理解できると思います。

環境によって使うミドルウェアを変えたい場合には IHostingEnvironment を使って、現在の環境を判別すればいいだけです。XDT で書き換えるよりも簡単ですね。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();
    
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

デフォルトでは開発環境の場合のみ開発用の詳細なエラーを表示するページを有効にしますが、それ以外の環境では単純なエラーページに飛ばすようにしています。

ここでも IApplicationBuilder に追加する順番が重要になってくるので、気をつけて設定したいです。大体はデフォルトのままで大丈夫ですが、ミドルウェアを追加する場合にはタイミングに気を付けましょう。

Dependency Injection

AddMvc や AddDbContext などフレームワークが提供している拡張メソッドも、実際は内部で必要なクラスをコンテナに追加しているにすぎません。デフォルトの DI コンテナでは 3 種類存在しています。

  • AddSingleton
    • アプリケーション中で 1 つだけインスタンス化
  • AddScoped
    • リクエスト中で 1 つだけインスタンス化
  • AddTransient
    • 常に新しくインスタンス化

デフォルトの DI コンテナはかなりシンプルなので、使いなれた DI コンテナに切り替えたい場合は、ConfigureServices メソッドで IServiceProvider を返すだけで良いみたいです。

基本的にはコンストラクタへのインジェクションのみで、プロパティへのインジェクションは途中で削除されたようですが、IServiceProvider をコンストラクタで受けとれば、任意のインスタンスを作成できそうです。