タイトルは私の問題を相談金曜日に同僚を反映して、私はこれは良い質問だと思います。この質問は、依存性注入フレームワークASP.NETコアのアプリケーションだけでなく、サービスインスタンスのライフサイクルを理解するために私たちを支援します。
まず、問題の再発します
簡単な例で発生した問題をシミュレートするために、私たちの同僚。私たちは、ASP.NET MVCのコアアプリケーションを作成するには、次のミニマリストの方法を使用します。次のコードに示すように、ASP.NETコアMVCフレームワークに関連するサービスやミドルウェアの登録に加えて、我々はまた、オプションServiceProviderOptions ValidateScopesプロパティはサービスの範囲のために開くために、Trueに設定された構成のUseDefaultServiceProvider IHostBuilderメソッドを呼び出します検証。また、スコープのライフサイクルモデル登録サービスIFoobar、foobarにもIDisposableインターを実装し、具体的な実装タイプを使用します。
パブリック クラスプログラム { パブリック 静的な 無効メイン() { ホスト .CreateDefaultBuilder() .UseDefaultServiceProvider(オプション => options.ValidateScopes = 真) .ConfigureWebHostDefaults(ビルダー => ビルダー .ConfigureLogging(ログ => logging.ClearProviders()) .ConfigureServices(サービス => サービスが .AddScoped <IFoobar、FOOBAR> () ().AddRouting .AddControllers()) .Configure(APP => アプリ .UseRouting() .UseEndpoints(エンドポイント => endpoints.MapControllers()))) .Build() .RUN(); } } パブリック インターフェイスIFoobar {} パブリック クラスFOOBAR:IFoobar、IDisposableを { 公共 ボイド廃棄()=> Console.WriteLineを(" Foobar.Dispose(); " ); }
私たちは、次のようにHomeController作成し、そのコンストラクタは、IServiceProviderオブジェクトを注入しました。アクションIndexメソッドでは、我々はいくつかの操作のタスクを実行し、非同期実行の静的メソッドを呼び出します。GetRequiredService <T>メソッド具体的には、操作非同期実行は、我々は、上記の注入はIFoobarサービスインスタンスを取得するため、このIServiceProviderオブジェクト試行を呼び出して使用します。、スタック情報スローされた例外メッセージがコンソールに直接実行のtry / catchでこの動作により出力されます。
パブリック クラスにHomeController:コントローラ { プライベート 読み取り専用IServiceProvider _requestServices。 公的にHomeController(IServiceProvider requestServices) { _requestServices = requestServices。 } [HTTPGET(" / " )] パブリックIActionResultインデックス() { Task.Run(非同期()=> { 試み { のawait Task.Delay(100 ); VARfoobarに= _requestServices.GetRequiredService <IFoobar> (); } キャッチ(例外例) { Console.WriteLineを(ex.Message)。 Console.WriteLineを(ex.StackTrace)。 } })。 戻る[OK]を(); } }
アプリケーションを実行した後、我々はブラウザアクションインデックスアクセスメソッドの打ち上げ後のルートパスを(「/」)を使用して使用し、次のエラーメッセージがサーバーコンソールに表示されます。
二、ApplicationServices与RequestServices
グラフ上に表示されるエラーメッセージから、我々は我々が必要とするサービスインスタンスを取得するためにIServiceProviderの処分を使用しようとしている問題を見ることができます。私たちは、ASP.NETのコアアプリケーションの起動と要求処理に必要なサービス担当者は、ほとんどの場合IServiceProvider DIコンテナによって提供されている、知っています。具体的には、ここでオブジェクトIServiceProvider A一貫した現在のアプリケーションのライフサイクルの2種類があり、我々は一般ApplicationServices呼ばれる、他の特定のIServiceProvider我々は意志、各要求のためのオブジェクトでありますRequestServicesと呼ばれます。
一般に、ApplicationServicesは、必要なサービス・インスタンス・ビルドプロセスを提供するための導管、必要なサービス要求処理の具体例としては、典型的にはRequestServicesによって提供されます。具体的には、各受信された要求のために、ASP.NETコアApplicationServicesフレームはRequestServicesのカプセル化であるIServiceScopeオブジェクトに代わってサービスの範囲を作成するために使用します。サービスの要求された範囲が終了したため、処理が完了すると、RequestServicesは廃棄すること。
例えば、我々は実証されている、現在の要求を処理するための時間が終了した使用RequestServicesための他のバックグラウンドスレッドで実行され、使用されているので、我々は人間であるためにHomeControllerコンストラクタIServiceProviderは(RequestServicesあるに注入されます100ミリ秒)待機し、異常が自然発生の上に示します。
第三に、ApplicationServicesを取得する方法
リクエストがRequestServicesを結合することはできませんので、ApplicationServicesは、我々はそれを得るために、後者の方法をバンドルされたアプリケーションを使用することができますか?ASP.NETコア3 iHostホストインタフェースは、我々はそれを返しApplicationServicesを必要と正式に次の属性サービスを持って示し、IHost / IHostBuilderベースのベアラを使用して。
パブリック インターフェースIHost:IDisposableを { タスクStartAsync(CancellationToken cancellationToken = 新しいCancellationToken())。 タスクStopAsync(CancellationToken cancellationToken = 新しいCancellationToken()); IServiceProviderサービス{ 取得します。} }
私たちのプログラムのプレゼンテーションのために、私たちはApplicationServicesオブジェクトを取得し、間接的に、このように建設にHomeControllerでIHostサービス注入、次の方法を採用することができます。
パブリック クラスにHomeController:コントローラ { プライベート 読み取り専用IServiceProvider _applicationServices。 公的にHomeController(IHostホスト) { _applicationServices = host.Services。 } [HTTPGET(" / " )] パブリックIActionResultインデックス() { Task.Run(非同期()=> { 試み { のawait Task.Delay(100 ); VAR foobarに= _applicationServices.GetRequiredService <IFoobar>(); } キャッチ(例外例) { Console.WriteLineを(ex.Message)。 Console.WriteLineを(ex.StackTrace)。 } })。 戻る[OK]を(); } }
我々はApplicationServicesにRequestServicesを交換するための方法として採用した場合、我々はそれが解決されるかどうかを疑問視?上記の試験と同様の後、私たちは、次のエラーメッセージがサーバーコンソールに表示されます見つけます。
第四に、サービスインスタンスのライフサイクル
上記の問題は、私たちの代表者によって引き起こされる「ルートコンテナ、」IServiceProviderは、スコープのサービスインスタンスのライフ・サイクル・モデルを解決するためにオブジェクトを使用しようとしました「の具体的な原因につながる依存性の注入[8]:サービスインスタンスのライフ・サイクル」は非常になされていますオフにします。この問題を解決するために、我々は、サービスの範囲内で必要な「サービスエリア」ApplicationServicesによると、抽出サービスインスタンスを作成する必要があります。リサイクルすることができ、通常のサービスインスタンスを確保するために、我々はまた、IServiceScopeはタイムリーな終わりをオブジェクトサービスの範囲を代表するものでなければなりません。正しいプログラミングモードを以下に示します。
public class HomeController: Controller { private readonly IServiceProvider _applicationServices; public HomeController(IHost host) { _applicationServices = host.Services; } [HttpGet("/")] public IActionResult Index() { Task.Run(async() => { await Task.Delay(100); using (var scope = _applicationServices.CreateScope()) { varfoobarに= scope.ServiceProvider.GetRequiredService <IFoobar> (); } })。 戻る[OK]を(); } }