この記事の目的は、リクエストの存続期間を通じて依存関係の単一のコンテナー(IoCコンテナー)を作成し、その作成と破棄を制御できる実用的なソリューションを見つけることです。

これは、Webアプリケーションがトランザクション対応である必要がある場合(そして、私の意見では、Webアプリケーションがそれを保持する必要がある場合、つまり、要求が正常に処理された場合にのみ変更を(たとえば、データベースに)適用し、いずれかの段階でエラーが発生し、誤った結果と制御不能な結果を示してい
ます )(
githubソースコード )。
理論
Web API 2プロジェクトは、着信リクエストパイプラインの構築を支援するように設計されたIAppBuilder OWINインターフェイスを使用して構成されます。
上記の画像は、リクエストのライフサイクルを示しています-チェーンのすべてのコンポーネントを通過し、Web API(別のコンポーネントでもあります)に戻り、サーバーからの応答を形成、装飾します。
単一の依存関係コンテナーを使用するには、要求処理の開始時に明示的に作成し、完了時に破棄する必要があります。
- リクエスト処理の開始。
- コンテナ作成;
- ミドルウェアでのコンテナの使用。
- Web APIでコンテナーを使用します。
- コンテナの破壊。
- 要求処理の完了。
これを行うには、コンテナを構成し、Web APIに登録するだけで十分です(DependencyResolverを使用)。
子コンテナを作成する独自のミドルウェアを作成します。
public class UnityContainerPerRequestMiddleware : OwinMiddleware { public UnityContainerPerRequestMiddleware(OwinMiddleware next, IUnityContainer container) : base(next) { _next = next; _container = container; } public override async Task Invoke(IOwinContext context) {
それを他のミドルウェアで使用します(私の実装では、context.Setを使用してグローバルOwinContextにコンテナを保存します。context.Setは次の各ミドルウェアに渡され、context.Getを使用して取得されます)。
public class CustomMiddleware : OwinMiddleware { public CustomMiddleware(OwinMiddleware next) : base(next) { _next = next; } public override async Task Invoke(IOwinContext context) {
1つの
BUTでない場合、これを完了することができます。
問題
ミドルウェアWeb APIには、次のような独自のリクエスト処理サイクルが内部的にあります。
- 要求はHttpServerに送られ、HttpRequestMessageの処理を開始してルーティングシステムに転送します。
- HttpRoutingDispatcherは、リクエストからデータを取得し、ルートテーブルを使用して処理を行うコントローラーを決定します。
- HttpControllerDispatcherで、以前に定義されたコントローラーが作成され、要求処理メソッドが呼び出されてHttpResponseMessageが形成されます。
DefaultHttpControllerActivatorの
次の行は、コントローラーの作成を担当します。
IHttpController instance = (IHttpController)request.GetDependencyScope().GetService(controllerType);
GetDependencyScopeメソッドの
メインコンテンツ :
public static IDependencyScope GetDependencyScope(this HttpRequestMessage request) {
Web APIがDependencyResolverを要求し、HttpConfigurationで登録し、dependencyResolver.BeginScope()を使用して子コンテナーを作成し、その中に要求の処理を担当するコントローラーのインスタンスが作成されることがわかります。
私たちにとって、これは次を意味します:ミドルウェアとWeb APIで使用するコンテナは同じではありません-さらに、それらは同じレベルのネストにあり、グローバルコンテナはそれらの共通の親です。 :
- グローバルコンテナ。
- UnityContainerPerRequestMiddlewareで作成された子コンテナー。
- Web APIで作成された子コンテナー。
Web APIの場合、これがリクエストを処理する唯一の場所である場合、これは非常に論理的に見えます。コンテナは最初に作成され、最後に破棄されます(これはまさに私たちが達成しようとしていることです)。
ただし、現時点では、Web APIはパイプライン内のリンクの1つにすぎません。つまり、独自のコンテナーの作成を放棄する必要があります-私たちのタスクは、この動作を再定義し、コントローラーを作成して依存関係を解決するためにWeb APIが必要なコンテナーを指定することです
解決策
上記の問題を解決するために、独自のIHttpControllerActivatorを実装することができます。このメソッドの作成メソッドには、以前に作成され、既にその中にある依存関係を解決するコンテナを受け取ります。
public class ControllerActivator : IHttpControllerActivator { public IHttpController Create( HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType ) {
Web APIで使用するために残っているのは、構成内の標準のHttpControllerActivatorを置き換えることだけです。
var config = new HttpConfiguration { DependencyResolver = new UnityDependencyResolver(container) };
したがって、単一のコンテナを操作するための次のメカニズムを取得します。
1.リクエストの処理の開始。
2.グローバルから子コンテナを作成します。
var childContainer = _container.CreateChildContainer();
3.コンテナをOwinContextに割り当てます。
context.Set(HttpApplicationKey.OwinPerRequestUnityContainerKey, childContainer);
4.他のミドルウェアでのコンテナの使用。
var container = context.Get<IUnityContainer>(HttpApplicationKey.OwinPerRequestUnityContainerKey);
5. Web APIでコンテナーを使用します。
5.1。 OwinContextからコントローラーを取得する:
var container = request.GetOwinContext().Get<IUnityContainer>(HttpApplicationKey.OwinPerRequestUnityContainerKey);
5.2。 このコンテナーに基づいてコントローラーを作成します。
var controller = (IHttpController)container.Resolve(controllerType);
6.コンテナの破壊:
childContainer.Dispose();
7.要求処理の完了。
結果
必要なライフサイクルに従って依存関係を構成します。
public static void RegisterTypes(IUnityContainer container) {
- ContainerControlledLifetimeManager-アプリケーション内に単一のインスタンスを作成します。
- HierarchicalLifetimeManager-コンテナー内に単一のインスタンスを作成します(コンテナーがHTTPリクエスト内で単一であることを確認しました)。
- TransientLifetimeManager-呼び出しごとにインスタンスを作成します(解決)。
上記の画像は、いくつかのHTTPリクエストのコンテキストでGetHashCodeの依存関係を示しています。
- AlwaysTheSame-アプリケーション内のシングルトンオブジェクト。
- SameInARequest-リクエスト内のシングルトンオブジェクト。
- AlwaysDifferent-各Resolveの新しいインスタンス。
»ソースは
githubで入手できます。
材料:
1. ASP.NET Web APIのパイプライン