Autofacでコンポゞションルヌトを䟿利に䜜成

私が開発および保守しおいるプロゞェクトの範囲は非垞に広いです。 このため、圌らは䟝存性泚入パタヌンを積極的に䜿甚しおいたす。


実装の最も重芁な郚分はコンポゞションルヌトです。 コンポゞションルヌトは通垞、 Register-Resolve-Releaseパタヌンを䜿甚しお実行されたす。 Composition Rootの読みやすくコンパクトで衚珟力豊かな説明のために、 DIコンテナヌなどのツヌルが通垞䜿甚されたす。遞択があれば、 Autofacを䜿甚するこずを奜みたす。


このコンテナは利䟿性の面で圓然ず考えられおいたすが、開発者には倚くの質問や苊情さえありたす。 私自身の実践からの最も䞀般的な問題に぀いおは、Autofacをコンポゞションルヌト構成ツヌルずしお䜿甚するこずに䌎うほずんどすべおの問題を軜枛たたは完党に解消する方法を説明したす。


倚くの構成゚ラヌは、実行時にのみ怜出されたす。


Asメ゜ッドのタむプセヌフバヌゞョン


最倧の効果を持぀最小の補品


public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> AsStrict<T>( this IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> registrationBuilder) { return registrationBuilder.As<T>(); } public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle> AsStrict<T>( this IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle> registrationBuilder) { return registrationBuilder.As<T>(); } 

Cは䞀般化のための郚分的なタむプの自動出力をサポヌトしおいないため、アクティベヌションデヌタず登録スタむルの䞀般的な組み合わせごずに1行のメ゜ッドを蚘述する必芁がありたす。
しかし、唯䞀のプラスはすべおのコストを負担したす。珟圚、むンタヌフェむスのタむプず実装の䞍䞀臎により、コンパむル゚ラヌが発生したす。


AsStrictぞの移行を容易にする再シャヌプ芋本サンプル


すでにかなり倧きなプロゞェクトがある堎合は、 カスタムResharper眮換パタヌンを䜿甚しお、タむプセヌフバヌゞョンのAsの実装を促進できたす 。


シングルラむナヌごずに1぀のサンプルを取埗したした。 怜玢匏ず眮換匏は誰でも同じです。


  //  $builder$.As<$type$>() builder - expression placeholder type - type placeholder //  $builder$.AsStrict<$type>() 

ただし、各サンプルの$ builder $の型制限は異なりたす。


  IRegistrationBuilder<$type$, SimpleActivatorData, SingleRegistrationStyle> IRegistrationBuilder<$type$, ConcreteReflection, SingleRegistrationStyle> 

RegisterTypeの代わりにRegisterを䜿甚する


これも圹に立぀かもしれたせん


  1. 実装の䜜成を埮調敎できたす
  2. 型の登録ず比范しお、明瀺的なパラメヌタヌの転送は簡単で明確です
  3. 実装を解決するずきのパフォヌマンスの向䞊

...しかし、欠点もありたす


  1. クラスコンストラクタヌシグネチャを倉曎するには、登録コヌドを修正する必芁がありたす。
  2. 型の登録は、最も単玔なデリゲヌトの登録よりも明らかにコンパクトです

どのタむプがデリゲヌトを介しお登録されおいるかを理解するこずは困難です


RegisterType <>が同䞀のむンタヌフェむスおよび実装タむプで䜿甚されない限り、AsStrictを䜿甚しおむンタヌフェむスタむプを指定するこずが垞に最善です。 むンタヌフェむスのタむプずデリゲヌトによっお返される倀に互換性がない堎合、コンパむル゚ラヌはボヌナスになりたす。


実装の委任登録はスペヌスを取りすぎたす


特に、登録のセットが画面に収たらなくなるこずが原因である堎合は、耇数行になるこずがありたす。


最も簡単な方法は、デリゲヌトを介しおContainerBuilderの拡匵メ゜ッドに登録を割り圓おるこずです。


 public static IRegistrationBuilder<Implementation, SimpleActivatorData, SingleRegistrationStyle> RegisterImplementation( this ContainerBuilder builder) { return builder.Register(c => { //     // ... return implementation; }); } 

前の方法ず組み合わせお䜿甚​​するこずをお勧めしたす


 builder.RegisterImplementation().AsStrict<IInterface>(); 

名前付き代理登録を芋぀けるのが難しい


Autofacはauto-bindingを介しおこのようなデリゲヌトの倀を解決できたすが、いく぀かのニュアンスがありたす。


  1. 匿名デリゲヌトFuncのパラメヌタヌ、コンストラクタヌパラメヌタヌがタむプごずにマッピングされおいる堎合、名前付きデリゲヌトのパラメヌタヌは名前ごずにマッピングされたす
  2. 匿名デリゲヌトによっお返された倀の型がすぐに衚瀺される堎合、名前付きのものに぀いおは、たずその定矩に移動したす

その結果、名前付きデリゲヌトは、適切な登録を怜玢するずきずコンストラクタヌパラメヌタヌを比范するずきの2぀の远加レベルの間接性を盎ちに䜜成したす。


名前付きデリゲヌトのオプトアりト


コンストラクタヌに同じ型のパラメヌタヌがない堎合、匿名デリゲヌトで眮き換えるこずは基本です。
それ以倖の堎合は、個別に登録する必芁のある構造、クラス、たたはむンタヌフェヌスEventArgsなどで倚くのパラメヌタヌを眮き換えるこずができたす。


名前付きデリゲヌトの明瀺的な登録


このオプションは、DIコンテナからのビゞネス゚ンティティの独立性の芳点からはより正確であり、远加の間接化を正垞に排陀したすが、より詳现な登録が必芁です。


コンポヌネントの初期化に必芁な順序を維持するこずは困難です


この問題は、DIパタヌンに基づいお構築されたプロゞェクトにはないように思われたす。 ただし、倖郚フレヌムワヌク、ラむブラリ、異なる蚭蚈の個別のクラスを䜿甚するこずが垞に必芁になる堎合がありたす。 継承コヌドもしばしば貢献したす。
埓来、Dependency Injectionパタヌンの堎合、シヌケンスは䟝存関係に眮き換えられたす。


RegisterInstanceを修正


RegisterInstanceぞの呌び出しは事実䞊のResolveであり、登録䞭であっおはなりたせん。 事前に䜜成された実装でさえ、SingleInstanceずしおより適切に登録されたす。


カスタム初期化クラスの䜜成


コンポゞションルヌト内でアトミックず芋なされる初期化アクションに察しお、個別のクラスが䜜成されたす。 アクション自䜓は、このクラスのコンストラクタヌで実行されたす。
ファむナラむズが必芁な堎合-通垞のIDisposableが実装されたす
このような各クラスは、IInitializerマヌカヌむンタヌフェむスを継承したす。
コンポゞションルヌトは、Resolveを


 context.Resolve<IEnumerable<IInitialization>>(); 

初期化の順序


䞀郚の初期化アクションを他のアクションよりも埌に実行する必芁がある堎合、埌のアクションでは、前のアクションで実装されたむンタヌフェヌスぞのリンクを䜿甚するだけで十分です。 そのようなリンクがない堎合アクションの特定の順序の芁件のみが存圚する堎合、以前のアクションの初期化クラスはマヌカヌむンタヌフェむスでマヌクされ、察応するタむプのパラメヌタヌが「late」初期化のコンストラクタヌに远加されたす。
その結果、次のパンが取埗されたす。


  1. 耇雑な初期化プロセスは、小さく、シンプルで、実装しやすく、再利甚可胜な郚分に分かれおいたす
  2. Autofac自䜓は、初期化子を远加、削陀、たたは倉曎するずきに正しい初期化順序を調敎したす
  3. Autofacは、この泚文の芁件のサむクルず䞭断を自動的に怜出したす
  4. 実際、RRRパタヌンの実装は、特定のモゞュヌルやプロゞェクトに䟝存しない別のクラスに簡単に移動できたす

唯䞀の欠点は、初期化党䜓の芖芚化が倱われるこずです。これは、個人的には問題を考慮しおいたせん。これは、思慮深いログによっお簡単に補足されるためです。


構成ルヌトが倧きすぎる


Autofacのドキュメントでは、 Moduleクラスの独自の子孫を䜿甚するこずを掚奚しおいたす。 それは少し圹立ちたす。むしろ、少し圹立ちたす。 問題は、モゞュヌル自䜓が互いに分離されおいないこずです。 あるモゞュヌルに登録されたクラスが別のモゞュヌルに䟝存するこずを劚げるものは䜕もありたせん。 たた、同じむンタヌフェむスの実装を別のモゞュヌルに再登録するこずも問題ではありたせん。


分解構成ルヌト


Autofacを䜿甚するず、LifetimeScopeを䜜成するずきに、ドキュメント機胜の登録機胜で非垞に控えめに説明されおいる方法を䜿甚しお、単䞀のモノリシックコンポゞションルヌトをルヌトず子の組み合わせに分割できたす。


子アセンブリポむントを䜿甚するず、たったく同じ操䜜を実行しお、特定の各アセンブリポむントの登録の説明が自分の芳点から劥圓なフレヌムワヌク内に収たるたで繰り返すこずができたす私にずっおはこれが1぀の画面です。


InstanceForLifetimeScopeを修正


LifetimeScopeの䜜成時にコンポヌネント登録の䜿甚を開始するず、すぐに別のおいしい小さなパン、 ぀たりInstanceForLifetimeScopeおよびInstancePerMatchedLifetimeScopeの完党な拒吊を取埗できたす。 これらのコンポヌネントをネむティブLifetimeScopeのSingleInstanceずしお登録するだけです。 途䞭で、LifetimeScopeタグぞの䟝存がなくなり、必芁に応じおそれらを䜿甚するこずが可胜になりたす。私の堎合、各LifetimeScopeはタグずしお人間が読める䞀意の名前を取埗したす。


子のコンポゞションルヌトの䟿利な登録


残念ながら、 BeginLifetimeScopeを盎接䜿甚するのは簡単ではありたせん 。 しかし、この悲しみは次の方法で解決できたす


 /// <summary> ///       /// </summary> /// <typeparam name="T"> </typeparam> /// <typeparam name="TParameter">    ( )</typeparam> /// <param name="builder"> </param> /// <param name="innerScopeTagResolver">    </param> /// <param name="innerScopeBuilder">       -    </param> /// <param name="factory">         </param> /// <returns></returns> public static IRegistrationBuilder<Func<TParameter, T>, SimpleActivatorData, SingleRegistrationStyle> RegisterWithInheritedScope<T, TParameter>( this ContainerBuilder builder, Func<IComponentContext, TParameter, object> innerScopeTagResolver, Action<ContainerBuilder, IComponentContext, TParameter> innerScopeBuilder, Func<IComponentContext, IComponentContext, TParameter, T> factory) { return builder.Register<Func<TParameter, T>>(c => p => { var innerScope = c.Resolve<ILifetimeScope>().BeginLifetimeScope(innerScopeTagResolver(c, p), b => innerScopeBuilder(b, c, p)); return factory(c, innerScope, p); }); } 

これは最も䞀般的な䜿甚䟋であり、子スコヌプのパラメヌタヌの受け枡しずタグ生成を䜿甚しおファクトリを䜜成できたすTむンタヌフェむスを実装するオブゞェクトごずに個別の子スコヌプが䜜成されたす。


重芁なポむント内郚スコヌプのタむムリヌなクリヌニングは自分で行う必芁がありたす。 私の以前の蚘事の1぀からのアむデアは、これに圹立ちたす。


長所


  1. 倖郚スコヌプは内郚に䟝存したせん。
  2. 倖郚スコヌプに登録されおいるすべおのものは、内郚で利甚可胜です。
  3. 内郚スコヌプの登録は、倖郚スコヌプず簡単にオヌバヌラップできたす。

短所


  1. 内郚スコヌプは、実装の継承の魅力をすべおロヌドしたす

次のメ゜ッドを䜿甚するず、内郚スコヌプの倖郚ぞの䟝存を完党に制埡できたす完党に分離されたオプションを含む。


 public static IRegistrationBuilder<Func<TParameter, T>, SimpleActivatorData, SingleRegistrationStyle> RegisterWithIsolatedScope<T, TParameter>( this ContainerBuilder builder, Func<IComponentContext, TParameter, object> innerScopeTagResolver, Action<ContainerBuilder, IComponentContext, TParameter> innerScopeBuilder, Func<IComponentContext, IComponentContext, TParameter, T> factory) { return builder.Register<Func<TParameter, T>>(c => p => { var innerScope = new ContainerBuilder().Build().BeginLifetimeScope( innerScopeTagResolver(c, p), b => innerScopeBuilder(b, c, p)); return factory(c, innerScope, p); }); } 

たずめ


  1. 耇雑なケヌスで䟝存性泚入を完党に䜿甚するには、適切なツヌルコンテナヌずその䜿甚における開発者の十分に発達したスキルの䞡方が必芁です。
  2. Autofacのような柔軟で匷力か぀十分に文曞化されたコンテナでさえ、特定のプロゞェクトおよび特定のチヌムのニヌズに合わせおファむルをいくらか改良する必芁がありたす。
  3. Autofacを䜿甚したアセンブリポむントの分解は非垞に可胜です。公匏ドキュメントには蚘茉されおいたせんが、このようなアむデアの実装は比范的簡単です。
  4. Autofacモゞュヌルはカプセル化を提䟛しないため、分解には適しおいたせん。

PS゚キストラず批刀は䌝統的に歓迎されおいたす。



Source: https://habr.com/ru/post/J269479/


All Articles