ASP.NET Core BlazorアプリケーションでのDIコンテナの利用

ASP.NET Core BlazorアプリケーションでのDIコンテナを利用するコードを紹介します。

概要

ASP.NET Core BlazorアプリケーションでDIコンテナを利用するコードを紹介します。
補足
ASP.NET Coreの Razor Pages でDIコンテナを利用する場合の例はこちらの記事を参照してください。

プログラム例

プロジェクトの作成

ASP.NET Core Webアプリケーションのプロジェクトを作成します。手順はこちらの記事を参照して下さい。

サービスクラスの追加

サービスクラスのコード MyService.cs を追加します。コードは下記です。
MyServiceクラスを実装します。MyServiceクラスはExecメソッドを1つ持つクラスです。また、MyServiceクラスの基底のインターフェイス IMyService も実装します。
MyService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DIContainer
{
  public interface IMyService
  {
    public string Exec();
  }

  public class MyService : IMyService
  {
    public string Exec()
    {
      return "Hello! Blazor App DI Container World!";
    }
  }
}

コードの編集

App.razor, _Imports.razor, Startup.cs のコードを編集します。
App.razor
<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" />
    </Found>
    <NotFound>
        <LayoutView>
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>
_Imports.razor
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop

Startup.csではConfigureServices() メソッドで services.AddSingleton メソッドを呼び出し先に実装した MyServiceクラスをDIコンテナに追加します。
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DIContainer
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddSingleton<IMyService, MyService>();
      services.AddRazorPages();
      services.AddServerSideBlazor();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.UseStaticFiles();
      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
      });
    }
  }
}

フォールバックページの追加

フォールバックページを追加します。
_Host.cshtml
@page
@namespace  DIContainer.Pages
<!DOCTYPE html>
<html lang="ja">
<head>
</head>
<body>
    <app>
        @(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
    </app>
    <script src="_framework/blazor.server.js"></script>
</body>
</html>

画面のrazorコンポーネントの追加

画面のrazorコンポーネントを追加します。今回の例では、Screen.razor ファイルとScreen.razorのモデルクラスを実装するScreen.razor.csファイルを追加します。コードは以下です。
Screen.razor
@page "/Screen"
@inherits ScreenModel
<h3>Index</h3>
<button @onclick="ButtonClick">Button1</button>
<p>@MessageText</p>
@code {

}

ScreenModelクラスは、ComponentBaseの派生クラスとして実装します。razorコンポーネントのコードビハインドについてはこちらの記事を参照して下さい。
DIコンテナに登録されたIMyService オブジェクトを参照する場合はクラスの記述で、Inject属性を利用してサービスのプロパティを宣言します。
Screen.razor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;

namespace DIContainer.Pages
{
  public class ScreenModel:ComponentBase
  {
    [Inject]
    public IMyService mysevice { get; set; }

    public string MessageText;

    public void ButtonClick()
    {
      MessageText = mysevice.Exec();
    }
  }
}

実行結果

プロジェクトを実行します。Webブラウザが起動しますので (アプリケーションルートURL)/Screen のURLにアクセスします。下図のページが表示されます。画面の[Button1]をクリックします。


画面のボタンの下に "Hello! Blazor App DI Container World!" の文字列が表示されます。DIコンテナからInject属性を設定した myservice変数にインスタンスが実行でき正しく値を返す動作になっていることが確認できます。

プログラム例: razorコンポーネントページでオブジェクトをインジェクトして参照する場合

先の例では、razorコンポーネントのページに対するモデルクラスがある場合のコードを紹介しました。Blazorアプリケーションではrazorコンポーネントのページに対してモデルクラスが無い場合や、razorコンポーネント内の@codeセクションのコードからインジェクションしたオブジェクトを参照したいこともあります。
razorコンポーネントページでオブジェクトをインジェクトして参照する場合は、razorコンポーネントのページに @Inject ディレクティブを記述します。

コード

razorコンポーネントを追加しページを作成します。
ScreenLight.razor
@page "/ScreenLight"
@inject IMyService ms
  <h3>ScreenLight</h3>
<button @onclick="Button2Click">Button2</button>
<p>@messageText</p>
@code {
  string messageText;

  void Button2Click()
  {
    messageText = ms.Exec();
  }
}

解説

ScreenLight.razorScreen.razor と同じ動作をするページです。Screen.razorはモデルクラスが別ファイルで存在します。一方 ScreenLight.razor はボタンクリック後のロジックもrazorファイル中に実装しています。そのため、razorファイル内に、MyServiceオブジェクトをインジェクトして参照する必要があります。

razorファイルで下記の@injectディレクティブによりオブジェクトをインジェクトしてrazorファイル中で参照できます。msがインスタンス化されたオブジェクトになります。
@inject IMyService ms

ms.Exec() の記述により、MyServiceクラスの Exec メソッドが呼び出され、文字列を返します。返された文字列を画面に表示します。
  void Button2Click()
  {
    messageText = ms.Exec();
  }

実行結果

プロジェクトを実行します。(アプリケーションルートURL)/ScreenLight にWebブラウザでアクセスします。下図のページが表示されます。


ページの[Button2]をクリックします。MyServiceクラスのExecメソッドが呼び出され、戻り値の文字列がページに表示されます。

プログラム例: インジェクションするサービスが複数ある場合

インジェクションするサービスが複数の場合です。上記のコードに YourService.cs クラスを追加します。
コードは以下です。
YourService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DIContainer
{
  public interface IYourService
  {
    public string Exec();
  }

  public class YourService : IYourService
  {
    public string Exec()
    {
      return "DIコンテナの世界にようこそ。";
    }
  }
}

Startup.cs にYourServiceクラスをDIコンテナに追加するコードを記述します。
Configureメソッドにservices.AddSingleton<IYourService, YourService>();を追記し下記の通り変更します。
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddSingleton<IMyService, MyService>();
      services.AddSingleton<IYourService, YourService>();
      services.AddRazorPages();
      services.AddServerSideBlazor();
    }

Blazorアプリケーションの画面(ページ)を追加します。ScreenMulti.razor の名称でrazorコンポーネントを追加します。
ScreenMulti.razor では @page "/ScreenMulti"により、(アプリケーションルートURL)/ScreenMulti にアクセスした際にこの画面を表示します。@inject ディレクティブにより、IMyService, IYourService の2つのオブジェクトをインジェクトして参照できる状態にしています。ボタンクリック時の処理では、IMyServiceのExecメソッドと、IYourService のExecメソッドを呼び出し、2つの結果を結合して画面に表示しています。
ScreenMulti.razor
@page "/ScreenMulti"
@inject IMyService ms
@inject IYourService ys

<h3>ScreenMulti</h3>
<button @onclick="Button3Click">Button3</button>
<p>@messageText</p>
@code {
  string messageText;

  void Button3Click()
  {
    messageText = ms.Exec() +" : "+ ys.Exec();
  }
}

実行結果

プロジェクトを実行します。Webブラウザで (アプリケーションルートURL)/ScreenMulti のURLにアクセスします。下図の画面が表示されます。


[Button3]をクリックします。画面に"Hello! Blazor App DI Container World! : DIコンテナの世界にようこそ。" のメッセージが表示され、MyServiceクラスのExecメソッドの戻り値と、YourServiceクラスのExecメソッドの戻り値との両方の値が画面に表示されます。


プログラム例: IWebHostEnvironmentのインジェクション

IWebHostEnvironment をインジェクションするコードを紹介します。

概要

先のコードでは自分で実装したクラスをインジェクションして利用しましたが、ASP.NET Core のフレームワークでインジェクションできるフレームワークが提供するサービスがあります。IWebHostEnvironment はフレームワークが提供するサービスの一つでアプリケーション名やアプリケーションの配置パスなどの環境情報を参照できるサービスです。このサービスをインジェクションしてページモデルなどで利用するコードを紹介します。

コード

上記のプログラムに OurService.cs ファイルを追加します。下記のコードを記述します。
OurService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;

namespace DIContainer
{
  public interface IOurService
  {
    public string Exec();
  }

  public class OurService:IOurService
  {
    IWebHostEnvironment _env;
    IMyService _ms;
    IYourService _ys;

    public OurService(IWebHostEnvironment env, IMyService ms, IYourService ys)
    {
      _env = env;
      _ms = ms;
      _ys = ys;
    }
    
    public string Exec()
    {
      return _ms.Exec() + " : " + _ys.Exec() + " : " + _env.ApplicationName;
    }
  }
}

Startup.csファイルのConfigureメソッドを変更し、先に作成した OurService クラスをDIコンテナに追加します。
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DIContainer
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddSingleton<IMyService, MyService>();
      services.AddSingleton<IYourService, YourService>();
      services.AddSingleton<IOurService, OurService>();
      services.AddRazorPages();
      services.AddServerSideBlazor();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.UseStaticFiles();
      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
      });
    }
  }
}

画面を追加します。
@page "/ScreenEnv"により、アプリケーションルートURL下の ScreenEnv ディレクトリアクセス時に画面を表示します。画面内のボタンをクリックした際には、インジェクションにより参照される、OurServiceクラスの Exec() メソッドを呼び出します。
ScreenWebHostEnv.razor
@page "/ScreenEnv"
@inject IOurService os

<h3>ScreenEnviroment</h3>
<button @onclick="Button3Click">Button3</button>
<p>@messageText</p>
@code {
  string messageText;

  void Button3Click()
  {
    messageText = os.Exec();
  }
}

解説

/ScreenEnv 画面のButton3クリックにより、OurServiceクラスのExecメソッドが呼び出されます。
OurServiceのコンストラクタでは、IWebHostEnvironment, IMyService, IYourService の3つのパラメーターをとります。コンストラクタで受け取ったオブジェクトはメンバ変数の_env, _ms, _ys に代入して保持します。
Exec()メソッドが呼び出された時点で、WebHostEnvironmentクラスのApplicationName, MyServiceクラスのExec()メソッド, YourServiceクラスのExec()メソッドを呼び出し、それぞれの戻り値の文字列を連結して画面に表示します。
  public class OurService:IOurService
  {
    IWebHostEnvironment _env;
    IMyService _ms;
    IYourService _ys;

    public OurService(IWebHostEnvironment env, IMyService ms, IYourService ys)
    {
      _env = env;
      _ms = ms;
      _ys = ys;
    }
    
    public string Exec()
    {
      return _ms.Exec() + " : " + _ys.Exec() + " : " + _env.ApplicationName;
    }
  }

実行結果

プロジェクトを実行し、Webブラウザで (アプリケーションルートURL)/ScreenMulti のURLにアクセスします。下図の画面が表示されます。


[Button3]をクリックします。OurServiceクラスのExecメソッドが呼び出されメソッドの戻り値が画面に表示されます。

DIコンテナ、インジェクションのメリット

DIコンテナやインジェクションのメリットとしては次のものがあります。

フレームワークのサービスオブジェクトを簡単に参照できる

ASP.NET Coreではフレームワークが提供するサービスオブジェクトがあります。必要となるサービスオブジェクトを@injectディレクティブやInjet属性、コンストラクタインジェクションで簡単に追加できます。

必要なクラスに対して必要となる参照オブジェクトを簡単に追加できる

DIコンテナに登録すれば、コンストラクタに参照したいオブジェクトを追加するだけで簡単にオブジェクトの参照を追加できます。

参照先のクラスを参照元でインスタンス化しない

DIを利用したコードでは、インスタンス化されたオブジェクトがコンストラクタの引数で渡されます。そのため、参照元のクラスでインスタンスを作成する必要が無いため、参照先のクラスの参照も不要になります。

クラス自体を参照せずにメソッドの呼び出しができる

クラスのインスタンスをクラス型の変数に格納すると、クラスへの参照が必要になりますが、DIでは参照先のクラスの基底のインターフェイス型の変数を利用してインスタンスオブジェクトを格納します。このため、メソッドの呼び出しなどにおいてもクラスの参照が不要になります。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2021-09-23
作成日: 2019-11-13
iPentec all rights reserverd.