1. ホーム
  2. Web プログラミング
  3. ASP.NET

ASP.NET Coreで複数のサービス実装クラスをインジェクトする方法

2022-01-14 18:34:04

前置き

依存性注入はASP.NET Coreで重要な役割を果たし、一般原則は「私が欲しいものは何でも送ってくれ」という高レベルなプログラミングのアイデアです。サービスタイプのインスタンスはコンテナによって自動的に管理され、コード内で明示的に処理する必要はありません。

ですから、依存性注入では、プログラミングについてこれまでとは異なる考え方をする必要があります。以前は、多くの関数型(暗号化や復号化を行うクラスなど)を静的に定義するのが好きでしたが、依存性注入を使えば、静的な型を避けて、サービスコンテナに管理を任せることができ、依存性注入をうまく使えば、便利だと感じるはずです。

依存性注入の主な、そしてより標準的な遊び方は、2つのパターンを持つことです。

  1. 10世代シングルトンモード。 インターフェースはクラスに対応し、例えば、最初にインターフェースIA, IBを定義し、その後、クラスAがIAを実装し、クラスBがIBを実装する。また、抽象クラス(または基底クラス)Eとし、FがクラスEを継承することもできる。
  2. Disjunctionパターン。 派生に関係なく、直接クラスを書き、サービスコンテナに直接追加すればよい。

ここで、例を見てみましょう。

1. インターフェイスの定義

    public interface IPlayGame
    {
        void Play();
    }




そして、それを実装するためのクラスを書きます。

    public class NBPlayGame : IPlayGame
    {
        public void Play()
        {
            Console.WriteLine("All people play anesthesia. ");
        }
    }



ご存知のように、いわゆるサービスクラスは、実は普通のクラスで、一般にMD5値の計算など、特定の機能を実行するために使用されます。次に、StartupクラスにはConfigureServicesメソッドがあることを思い出してください。そう、そこで先ほどのサービスを登録します(つまり、ServiceCollectionコレクションに追加します)。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IPlayGame, NBPlayGame>();
        }




追加は、IPlayGame インターフェースが NBPlayGame クラスに対応する、1 対 1 のシンプルなタイプです。追加時に呼び出すことのできる3つのメソッドがあり、実質的にコンテナ内のサービスクラスのライフサイクルに対応しています。

  • AddSingleton. 最も寿命が長く、空とともに生きる単一のインスタンス。アプリケーション全体では1つのインスタンスしか使用されない。
  • AddTransient : こちらは、毎晩遅くまで働いているせいか、すぐに死んでしまうので、一番短命です。この場合、サービスクラスのインスタンスは使用時に生成され、使用後に直接破棄される。
  • AddScoped : これは理解するのが難しいですね。これは、クライアントとサーバー間の後続のサブリクエストを含む、単一のリクエスト内のライフサイクルを持ち、とにかく要求されたセッションが終了するとすぐにクリーンアップされます。

2. サービスの注入

例えば、ミドルウェア上、コントローラ上、他のサービスクラスのコンストラクタ上(ミドルウェアはInvoke / InvokeAsyncメソッド上)などでインスタンス受信を行うことができます。

さて、実際に使うには、ミドルウェアを書きます。

    public class TestMiddleware
    {
        public TestMiddleware(RequestDelegate next) { }

        public Task InvokeAsync(HttpContext context, IPlayGame game)
        {
            game.Play();
            return Task.CompletedTask;
        }
    }


登録されたサービスは、InvokeAsyncメソッドのパラメータにインジェクションされます。最初のパラメータはHttpContextで、これは必須パラメータであり、それ以降はインジェクションされたパラメータであることに注意してください。

最後に、StartupクラスのConfigureメソッドでミドルウェアを使用することができます。

        public void Configure(IApplicationBuilder app)
        {
            app.UseMiddleware<TestMiddleware>();
        }
 



実行後、Playメソッドが呼び出され、コンソールに以下の結果が出力されます。

 3. 機能性クラス

 disjoint"パターンとも呼ばれ、インターフェース仕様を使用せずに直接関数クラスを記述します。

    public class DoSomething
    {
        public string GetMessage() => "Hello, just Boss looking for you. ";
    }



サービス登録時の簡便化。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<DoSomething>();
        }



Configureメソッドでインジェクトします。

        public void Configure(IApplicationBuilder app, DoSomething thing)
        {
            Console.WriteLine(thing.GetMessage());
        }




実行後、出力は次のようになります。

 コンテナ内では、ServiceDescriptor クラスを使用して、サービスのタイプに関する情報を格納します。ServiceType がサービスの種類を示す場合、サービスがインターフェースと実装クラスを持つ場合、このプロパティはインターフェースの種類を参照し、実装クラスの種類情報は ImplementationType プロパティによって格納されます。インタフェースが存在せず、型のみが直接定義されている場合、この型の情報は ServiceType プロパティに格納され、ImplementationType プロパティは使用されない。

上記の例では、ServiceType が IPlayGame インターフェースに関する情報、ImplementationType が NBPlayGame クラスに関する情報です。上記のDoSomethingクラスの場合のように、ServiceTypeがDoSomething関連の情報、ImplementationTypeが空となります。

 4. 上級クラス

次に、高度なプレイについて見ていきましょう。

インターフェイスを定義します。

    public interface IDemoService
    {
        string Version { get; }
        void Run();
    }




そして、このインタフェースを実装したクラスが2つ存在します。

    public class DemoService1 : IDemoService
    {
        public string Version => "v1";

        public void Run()
        {
            Console.WriteLine("The first service implementation class. ");
        }
    }

    public class DemoService2 : IDemoService
    {
        public string Version = > "v2";

        public void Run()
        {
            Console.WriteLine("Second service implementation class. ");
        }
    }



次に、サービスを登録します。

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IDemoService, DemoService1>();
            services.AddTransient<IDemoService, DemoService2>();
        }




次に、同じようにインジェクションを受け取りますが、その際もミドルウェアのメソッドパラメータを使って受け取ります。

    public class DemoMiddleware
    {
        public DemoMiddleware(RequestDelegate next)
        {
            // This constructor must be provided due to programmatic conventions.
        }

        public async Task InvokeAsync(HttpContext context, IDemoService sv)
        {
            await context.Response.WriteAsync(sv;)
        }
    }


そして、Startup.Configureメソッドでミドルウェアを使用します。

        public void Configure(IApplicationBuilder app, DoSomething thing)
        {
            app.UseMiddleware<DemoMiddleware>();
        }




実行後、問題を確認し、出力を見ます。

 何かが間違っています。パラメータは、最後に登録された実装タイプのインスタンス、つまりDemoService2クラスしか受け取ることができません。.NET Coreは、複数のサービス実装クラスのインジェクションをサポートしていないのでしょうか?これは多くの人にとって難しいことです。

実は、Core兄弟は実装クラスの複数インスタンスのインジェクションに対応しているんです。

ここで、オールド・チョウが2つの解決策を紹介します(実際には3つあり、特にコア・ブラザーに慣れていない場合は、なかなか手に入らないものもあるので、基本的には2つで十分と言うことにします)。

方法1:IServiceProvider型のインジェクションを受ける。

        public async Task InvokeAsync(HttpContext context, IServiceProvider provider)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var sv in provider.GetServices<IDemoService>())
            {
                sb.Append($"{sv.Version}<br/>");
            }
            await context.Response.WriteAsync(sb.ToString());
        }



IServiceProviderが参照するインスタンスを受け取ることができるのであれば、GetServicesメソッドで複数のサービスインスタンスを取得することが可能です。

OldWeekが推奨する方法2はよりシンプルで、IEnumerable<T>型(この場合はIEnumerable<IDemoService>)を直接インジェクトするものです。

        public async Task InvokeAsync(HttpContext context, IEnumerable<IDemoService> svs)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var sv in svs)
            {
                sb.Append($"{sv.Version}<br/>");
            }
            await context.Response.WriteAsync(sb.ToString());
        }



IEnumerable<T> の良さは、foreach ができるので、複数のインスタンスにもアクセスでき、必要に応じて LINQ で遊べることです。

結果は次のようになります。

ASP.NET Coreに複数のサービス実装クラスを注入する方法については、この記事にまとめています。ASP.NET Coreの詳細やASP.NET Coreに複数のサービス実装クラスを注入する方法については、スクリプトハウスの過去記事を検索するか、引き続き以下の関連記事を閲覧してください。