ASP.NET MVC:変換またはインジェクション? それが問題です

クラスは、具体的なクラスではなく、抽象クラスのみに依存する必要があります。 この問題を解決するために、2つのService LocatorとDependency Injectionのプラクティスから選択できます。 ASP.NET MVCは、サービスロケーターである「依存関係リゾルバー」を使用しますASP.NET MVCアプリケーションを開発する場合、解決を使用するか注入を使用するかを決定する必要があります。



依存関係の注入は、依存関係の問題を解決する上で非常に価値があることを理解していますが、それが唯一の本当の問題だとは思いません。 依存性注入は実装の詳細に過ぎず、実際に重要なのは、その基礎となる原理、つまり依存性の依存性反転の原理です。 頭字語SOLIDに精通している場合、依存関係の反転の原理は頭字語で単に「D」です。 ところで、イニシャルからの堅実な結果は、オブジェクト指向ソフトウェア開発に必要と考えられる5つの設計原則です。 これらの5つの原則は次のとおりです。



これらの原則のほとんどすべては、方向を示すという意味でのベクトルにすぎませんが、割り当てられたタスクの解決方法に関する具体的な指示は与えません。 単一義務の原則を例にとりましょう。 これはすべて、後で変更する理由が1つだけになるように、クラスを作成する必要があることを示しています。 原則の考え方は、クラスはそうでない場合よりもはるかにまとまりがあるべきだということです。 それらに含まれるメソッドは、論理的に接続され、単一の責任チェーンを形成する必要があります。 ご覧のとおり、この原則は明確で一般的なアイデアを示していますが、これを実現する方法については説明していません。



依存関係の逆転の原則


それどころか、依存関係の逆転の原理はかなり曖昧な定式化を持っていますが、実装ステップの詳細なセットに変換できます。 依存関係の反転の原則では、クラスは特定のクラスに依存関係を持つべきではなく、抽象化されている必要があると述べています。 より理解しやすい言語では、これは、クラス内のすべての重要な依存関係を抽象化するためにインターフェイスを使用する必要があることを意味します。 たとえば、クラスでロギングコンポーネントを使用する場合、これを行うためにできる最善のことは、LoggerクラスではなくILoggerインターフェイスを宣言することです。 したがって、メインコードを壊すことなく、いつでも(および必要な回数)ロギングクラスの実装を変更できます。

依存関係の反転の原則を実装するためのアルゴリズムの意味は次のとおりです。依存関係のリストをコード内の必要な場所に渡します。 例でこれを見てみましょう。

public class MyComponent { public MyComponent() { : } public void DoSomeWork() { var logger = new Logger(); : } }
      
      





明らかに、MyComponentクラスはLoggerクラスに依存しています。 ILoggerに変更する場合、インターフェイスを実装する実際のクラスへの参照を取得するにはどうすればよいですか?

 public class MyComponent { public MyComponent() { : } public void DoSomeWork() { ILogger logger = ...; // who's going to provide this? : } }
      
      





依存関係の反転の原則を実装するには、2つの主要なモデル(Service LocatorとDependency Injection)を選択できます。 最初のメソッドはクラス内の問題を解決し、2番目のメソッドはクラスから依存関係を導き出します。 次のリストは、Service Locatorメソッドを示しています。

 public class MyComponent { public MyComponent() { : } public void DoSomeWork() { ILogger logger = ServiceLocator.GetService(); : } }
      
      





通常、型(通常はインターフェイス)を受け取り、このインターフェイスを実装する特定の型のインスタンスを返す依存関係解決コンポーネントがあります。 渡される型の一致とインスタンス固有の型は、ロケーターコンポーネントの実装で非表示になります。 このモデルは、Service Locatorパターンと呼ばれます。

別のアプローチを示します。

 public class MyComponent { private ILogger _logger; public MyComponent(ILogger logger) { _logger = logger; } public void DoSomeWork() { // Use the logger component here _logger.Log(); : } }
      
      





この場合、MyComponentクラスは、外部の世界で使用するILoggerコンポーネントを取得します。 ご使用の環境は、レジストラをMyComponentに渡す前に、レジストラの初期化を処理します。 これが依存性注入モデルの本質です。

Dependency InjectionとService Locatorの違い(ある場合)は何ですか? 両方のモデルは、依存関係の反転の原則を実装するのに適しています。 Service Locatorモデルは既存のコードで使用する方が簡単です。これにより、パブリックインターフェイスを強制的に変更することなく、全体的な設計が弱くなります。 同じ理由で、Service Locatorモデルに基づいたコードは、Dependency Injectionに基づいた同等のコードよりも読みにくいです。

依存性注入は、クラス(またはメソッド)への注入の前後にどのタイプになるかを明確に示しています。 このため、結果として、コードはよりクリーンで読みやすくなります。 ASP.NET MVCはどうですか?



ASP.NET MVCでの依存性注入


ASP.NET MVCは複数の拡張ポイント用に設計されていますが、全体的には依存性注入の包括的なサポートを提供していません。 Service Locatorは、既存のシステムを新しい拡張ポイントの追加とより疎結合にする最も効果的な方法です。これは最も邪魔にならないソリューションだからです。 Service Locatorは、特定の時点で設定したブラックボックスのように機能し、ルール、必要な契約、およびそれらの取得方法を指定します。 ASP.NET MVCには、システムのコンポーネントである多くの拡張ポイントがありますが、カスタム拡張ポイントに置き換えることもできます。 この表には、ASP.NET MVC 3の既知の拡張ポイントがリストされています。



プロバイダー 説明
アクション呼び出し //コントローラクラスのコンストラクタでcontroller.ActionInvoker = new YourActionInvoker();
コントローラー工場 // global.asaxでは、Application_Start

var factory = new YourControllerFactory(); ControllerBuilder.Current.SetControllerFactory(工場);

辞書の値 // global.asaxで、

Application_Start var providerFactory = new YourValueProviderFactory(); ValueProviderFactories.Factories.Add(providerFactory);

モデルバインダー // global.asaxでApplication_Start ModelBinders.Binders.Add(typeof(YourType)、new YourTypeBinder());
モデルバインダープロバイダー // global.asaxでは、Application_Start var provider = new YourModelBinderProvider(); ModelBinderProviders.BinderProviders.Add(プロバイダー);
モデルのメタデータ // global.asaxでは、Application_Start ModelMetadataProviders.Current = new YourModelMetadataProvider();
モデル検証 // global.asaxでは、Application_Start var validator = new YourModelValidatorProvider(); ModelValidatorProviders.Providers.Add(バリデーター);
温度データ //コントローラークラスのコンストラクター内

controller.TempDataProvider = new YourTempDataProvider();

ビューエンジン // global.asaxでは、Application_Start ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(新しいYourViewEngine());




ビューエンジン// global.asaxで、Application_Start ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(新しいYourViewEngine());

ASP.NET MVC 3より前は、カスタムコンポーネントを登録する標準的な方法はありませんでした。 表1にリストされている各コンポーネントには、ユーザーアプリケーションに統合するための独自のAPIが必要です。 バージョン3以降、ASP.NET MVCは、アービター依存関係に基づいた新しい(オプション)モデルを導入します。 システムコンポーネントを交換するには、表1で説明されているパスに従うか、このタイプの注入(依存性注入)を登録します。 ASP.NETランタイムは依存関係を検出し、必要に応じて参照します。

依存関係の解決は、ASP.NET MVCコードと統合された単なるロケーターサービスです。 アービトレーターは、既存の(大規模な)コードに依存関係反転の原則の実装を追加する1つの方法です。 コードのサイズと複雑さについては、状態APIのさまざまなレベルでの変更が必要になるため、依存関係の使用はあまり適切ではありません。 これは、ASP.NET MVCなどの基本的なオプションではありません。 ASP.NET MVCでのアービター依存関係の実装について詳しく見ていきましょう。



依存関係リゾルバーの定義


ASP.NET MVC Dependency Resolverでは、次のインターフェイスを実装するオブジェクトです

 { Object GetService(Type serviceType); IEnumerable<Object> GetServices(Type serviceType); }
      
      





コンバーターに書き込むロジックは完全にユーザー次第です。 型の説明をチェックし、特定の型の新しく作成されたインスタンスを返す単純なロジックのようなものにすることができます。 これは、インスタンスの作成、構成ファイルからの情報の読み取り、またはリフレクションの使用のより複雑な実装によって実行できます。 最後に、それは単一性または他のIoCフレームワークに基づくことができます。 これは非常にシンプルですが機能的な変換の例です。

 public class SampleDependencyResolver : IDependencyResolver { public object GetService(Type serviceType) { if (serviceType == typeof(ISomeClass)) return new SomeClass(); : } public IEnumerable<object> GetServices(Type serviceType) { return Enumerable.Empty<Object>(); } }
      
      





次のコードは、コンバーターがUnity(およびファイル構成内のそのセクション)を使用して依存関係を解決する方法を示しています。

 public class UnityDependencyResolver : IDependencyResolver { private readonly IUnityContainer _container; public UnityDependencyResolver() : this(new UnityContainer().LoadConfiguration()) { } public UnityDependencyResolver(IUnityContainer container) { _container = container; } public Object GetService(Type serviceType) { return _container.Resolve(serviceType); } public IEnumerable<Object> GetServices(Type serviceType) { return _container.ResolveAll(serviceType); } }
      
      





DependencyResolverクラスとSetResolverメソッドを使用して、アクセス許可をASP.NET MVC構造に登録します。

 protected void Application_Start() { // Prepare and configure the IoC container var container = new UnityContainer(); : // Create and register the resolver var resolver = new UnityDependencyResolver(container); DependencyResolver.SetResolver(resolver); }
      
      





IoCフレームワークを許可から使用する場合、登録されたタイプのリストを提供するための最良の方法を見つける必要があります。 この情報を無料のコードで渡す場合は、名前を作成する前にIoCコンテナーを完全に構成する必要があります。 Unityのルールに従って、web.configファイルを使用してIoCを構成する場合、許可にデフォルトのコンストラクターを使用できます。これには、構成データのロードが必要です。 ただし、IoCの別のフレームワークをターゲットにしている場合は、このコードを変更する必要がある場合があることに注意してください。

一般に、解像度の依存関係は、システムコンポーネントではなく、開発者が独自のコンポーネントを展開するために追加で使用できる内部ツールです。 依存関係コンバーターの美しさは、ASP.NET MVCの使用に限定されています。 コンバーターは、わかりやすい目標を達成するために、既知の場所で呼び出されます。 言い換えると、ASP.NET MVCがキャッシュコントローラーなどを作成する前に変換を行わない場合、残念ながら組み込みキャッシュを独自のものに置き換えることはあまりできません。



アプリケーションでの変換の使用


依存関係の変換は、ASP.NET MVCがサービスの検出に使用する名前にすぎないことが判明しました。 しかし、とにかく実際のアプリケーションでどのように使用できますか? 答えは非常に簡単です。必要な形式ですべてのコスト体系を作成する必要があるコンバーターは1つだけです。 依存関係変換のもう1つのオプションを次に示しますが、その例は少し高かったです。

 public class SampleDependencyResolver : IDependencyResolver { public object GetService(Type serviceType) { try { return serviceType == typeof(ModelMetadataProvider) ? new ExtendedAnnotationsMetadataProvider() : Activator.CreateInstance(serviceType); } catch { return null; } } public IEnumerable<object> GetServices(Type serviceType) { return Enumerable.Empty<object>(); } }
      
      





GetServiceメソッドは型を受け取り、既知の型のリストでそれを確認します。 一部の既知のインターフェイスタイプでは、手動で作成された既知のタイプのインスタンスを単純に返すことができます。 他のタイプについては、何も返すことができません。これは、アービターがこのタイプで動作できないことを意味します。

ASP.NET MVCの依存関係マッパーは、アプリケーションのライフサイクル中に同じタイプを変換することはできません。 これは、一般的なロケーターサービスの実装における重大な制限になる可能性がありますが、特定のASP.NET MVCコンテキストでは大きな問題ではありません。 依存関係変換はASP.NET MVCの内部機能であり、ASP.NET MVCのみが登録されたコンバーターをいつどのように呼び出して使用するかを決定します。 最終的に、コンバーターは限られた数の場合にのみ呼び出され、インターフェイスは理解できる以上のものになります。



それでは、トランスフォームやインジェクションを使用するのは何ですか?


設計効率の観点からこの問題を見ると、設計を明確で明確な責任配分に導くため、依存性注入が推奨されます。 依存性注入を使用するには、APIを変更する自由が必要になる場合があります。 これは、コンテキストによっては受け入れられない場合があります。たとえば、ASP.NET MVC 2からASP.NET MVC 3に切り替える場合などは受け入れられません。このため、Microsoftは、依存関係コンバーターを使用することを選択しました。 Service Locatorコンポーネント より現実的な例として、ロケーターサービスは、大規模な既存のアプリケーションでリファクタリングする必要がある場合の唯一のオプションです。



psこれは私の理解のおかげで私の最初の翻訳です。



All Articles