StructureMap-䜜業のクむックリファレンス2/3

StructureMapに関する最初の投皿の続き

最初の郚分では、トピックに぀いお説明したした。

このパヌトでは、以䞋に぀いお説明したす。


コンストラクタヌ


IoCコンテナヌの䟝存関係の解決に関連する実際のプログラミングでの非垞に重芁な質問、いく぀かのコンストラクタヌを持぀クラスに぀いおはどうですか。 それらを初期化する方法、パラメヌタを蚭定する方法、倉曎する方法など。 以䞋の質問のほずんどが回答されるこずを望みたす。 StructureMapは本圓に匷力で柔軟です。

フレヌムワヌクの機胜を説明する前に、テストクラスに぀いお説明する必芁がありたす。 今回はより困難になりたす。 継承、単玔型ず耇合型のコンストラクタ。



そこで、次のクラスを甚意したしょう。
public interface IClassA : IClass { int A { get; set; } } public interface IClassB : IClass {} public class ClassA : IClassA { public int A { get; set; } public int B { get; set; } public Class1 Class1 { get; set; } [DefaultConstructor] public ClassA() {} public ClassA(int a) { A = a; } } public class ClassB : IClassB { public IClassA ClassA; public ClassB(IClassA classA) { ClassA = classA; } } public class ClassM : IClassA { public int A { get; set; } public ClassM(int a) { A = a; } public ClassM(int a, int b) { A = a + b; } } 

それらは珟時点でいく぀かの冗長性を備えおおり、ストヌリヌでもう少し圹立ちたす。

それでは、最も単玔なオプションから始めたしょう。

単玔型


たず、コンストラクタヌの1぀が敎数パラメヌタヌを受け入れるClassAクラスから始めたしょう。
 public class WithSimpeArguments { public IContainer Container; public WithSimpeArguments() { Container = new Container(x => x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) ); } } 

䟋からわかるように、倀を䜿甚しおコンストラクタヌを初期化する指瀺が、すでによく知られおいる宣蚀に远加されたす。 この堎合、int型のパラメヌタヌは1぀しかないため、コンストラクタヌ宣蚀は単玔化されたスキヌムに埓いたす。 初期化プロセスで、すぐに倀を瀺したした。 同時に、StructureMapは倀が敎数でなければならないこずを通知したす。

操䜜を確認するには、すでにおなじみのコヌドを呌び出すこずができたす
 private static string WithSimpleArgumentsExample() { var container = new WithSimpeArguments().Container; var classA = (ClassA) container.GetInstance<IClassA>(); return classA.A.ToString(); } 

その結果、数字の5がコン゜ヌルに衚瀺されたす。

コンストラクタヌに同じ型の2぀のパラメヌタヌがある堎合の䟋は耇雑です。 実隓クラスClassMずしお。
 public class WithMultipleSimpeArguments { public IContainer Container; public WithMultipleSimpeArguments() { Container = new Container(x => x.For<IClassA>().Use<ClassM>() .Ctor<int>("a").Is(6) .Ctor<int>("b").Is(5)); } } 

ここで、 .Ctor <int> "a"。Is6に匕数の名前が远加されおいるため、フレヌムワヌクは匕数の倀を正確に䞀臎させるこずができたす。 フレヌムワヌクは垞に最も「貪欲な」コンストラクタを芋぀け、すべおの匕数を初期化するこずを望みたす。 クラスの珟圚の実装では、2番目の匕数の倀の蚭定を省略するこずはできたせん。 ただし、デフォルトで䜿甚するコンストラクタヌをStructureMapに指瀺できたす。そのためには、 DefaultConstructor属性を䜿甚する必芁がありたす。

デフォルトのコンストラクタヌ


DefaultConstructor属性を䜿甚するず、クラスのむンスタンス化に䜿甚するコンストラクタヌを明瀺的に指定できたす。 そのため、前の䟋では倉数bの宣蚀を省略でき、プロセスに䜕も萜ちたせんでした。
 public class ClassM : IClassA { public int A { get; set; } [DefaultConstructor] public ClassM(int a) { A = a; } public ClassM(int a, int b) { A = a + b; } } 

これで、1぀のパラメヌタヌでコンストラクタヌを䜿甚できたす

耇合タむプ


構造型自䜓は、耇合型によるすべおの䟝存関係を怜出および解決するため、耇合型の操䜜は非垞に簡単です。 ぀たり 最初にクラスを芋るず、ClassBクラスがClassAクラスによっお初期化されおいるこずがわかりたす。 この皮類の䟝存関係を解決するために特別なものは必芁ないこずがわかりたした。
 public class WithObjectArguments { public IContainer Container; public WithObjectArguments() { Container = new Container(x => { x.For<IClassA>().Use<ClassA>().Ctor<int>().Is(5); x.For<IClassB>().Use<ClassB>(); }); } } 

䟋からわかるように、远加の挔算子は適甚されたせん。 ただし、ClassAによっお初期化されるClassBクラスを芁求できたす。
 private static string WithObjectArgumentsExample() { var container = new WithObjectArguments().Container; var classA = (ClassB) container.GetInstance<IClassB>(); return classA.ClassA.A.ToString(); } 

数字の5が画面に衚瀺されたす。

タむプキャスト


ClassBクラスの宣蚀を芋るず、コンストラクタヌの倉数が特定の型ではなくむンタヌフェむスであるこずがわかりたす。 むンタヌフェヌスの代わりに、コンストラクタヌがClassAクラスを受け入れるようにクラスを曞き盎したす
 public class ClassB : IClassB { public ClassB(ClassA classA) { ClassA = classA; } } 

コンテナが単䞀のデヌタ型を登録しお返すため、型バむンディングは発生したせん。 私たちの堎合、それはIClassAです。
 private static string WithObjectArgumentsForwardingAndWiringExample() { var container = new WithObjectArgumentsForwarding().Container; var classB = (ClassB) container.GetInstance<IClassB>(); return classB.ClassA.A.ToString(); } 

ClassAクラスのデフォルトコンストラクタヌが呌び出されるため、バむンディングは発生しなかったため、このコヌドは0を返したす。

あなたはそれが問題ではないず掚枬したした。 StructureMapが䜕に぀ながるかを指定できたす。 これは、゜ヌスタむプを指定する必芁のあるForwardコマンドを䜿甚しお行われたす。
 public class WithObjectArgumentsForwarding { public IContainer Container; public WithObjectArgumentsForwarding() { Container = new Container(x => { x.For<IClassA>().Use<ClassA>().Ctor<int>().Is(5); x.Forward<IClassA, ClassA>(); x.For<IClassB>().Use<ClassB>(); }); } } 

これで、WithObjectArgumentsForwardingAndWiringExampleメ゜ッドを呌び出しお、応答で5を取埗できたす。

匕数の定矩


ほずんどの堎合、事前にクラスの匕数を芋぀けお蚭定するこずは䞍可胜であり、必芁なクラスを䜜成した時点でのみ匕数が認識されたす。 そしお、これらはプログラムを曞く䜜業日であるため、クラスの䜜成時に匕数を蚭定するサポヌトは、StructureMapに衚瀺されたすが、助けにはなりたせん。

新しい匕数倀を䜿甚しおクラスを呌び出すプロセスは、Yodaマスタヌの蚀語での通信に䌌おいたすが、これはDSLの特異性です。 技術的な制限です。

したがっお、新しいClassAクラスでClassBクラスを呌び出しおみたしょう。 これを行うには、Withステヌトメントが必芁です。
 private static string WithObjectArgumentsOverridingExample() { var container = new WithObjectArgumentsForwarding().Container; var classB = (ClassB) container .With(new ClassA(8)) .GetInstance<IClassB>(); return classB.ClassA.A.ToString(); } 

この堎合、フレヌムワヌクはクラスむンスタンスをどの匕数にマップするかを明確に決定できるため、匕数の名前を指定する必芁はありたせん。

クラスに察しおより倚くの匕数を指定する必芁がある堎合は、パラメヌタヌ名ずその倀でより倚くのWithメ゜ッドを䜿甚する必芁がありたす。
 private static string WithObjectArgumentsOverridingExample() { var container = new WithObjectArgumentsForwarding().Container; var classB = (ClassB) container .With(new ClassA(8)) .With("s").EqualTo(5) .GetInstance<IClassB>(); return classB.ClassA.A.ToString(); } 

これで、デザむナヌずの䜜業は、アプリケヌションにあるほがすべおのアヌキテクチャアプロヌチを解決するのに十分にカバヌされたず思いたす。

プロパティ


必芁なコンストラクタヌパラメヌタヌに加えお、クラスむンスタンスを䜜成するずきにクラスプロパティの倀を蚭定するず非垞に䟿利です。 ぀たり 曞く代わりに
 var classA = new ClassA(); classA.A = 8; classA.B = 20; 

次のように、プロパティをより短く、よりきれいに蚭定できたす。
 var classA = new ClassA { A = 8, B = 20 }; 

StuctureMapの堎合、それは非垞に可胜であり、同じ゚レガントで理解しやすい方法です。

シンプルなプロパティ蚭定


䞊の図のように、最も単玔なケヌスでは、 SetPropertyメ゜ッドを䜿甚しおパラメヌタヌを蚭定できたす 。
 public class WithArgumentsSetProperty { public IContainer Container; public WithArgumentsSetProperty() { Container = new Container(x => x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) .SetProperty(p => { pA = 8; pB = 20; })); } } 

䟋からわかるように、プロパティは匷く型付けされおおり、intelliSenseヒントを䜿甚できたす。 プロパティの蚭定は簡単で簡単です。 すべおのプロパティを蚭定できるわけではなく、クラスのむンスタンスを構築する段階で初期化するプロパティのみを蚭定できるこずは明らかです。

組み蟌みプロパティ定矩


むンラむンパラメヌタ蚭定を䜿甚するには、 Setterメ゜ッドが䜿甚されたす。 この方法を䜿甚するず、䞀床に1぀のパラメヌタヌの倀を蚭定できたす。 メ゜ッドの匕数は関数であるため。

最も簡単なこずは、初期化のパラメヌタヌを明瀺的に蚭定するこずです。
 public class WithArgumentsSetterExplicit { public IContainer Container; public WithArgumentsSetterExplicit() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) .Setter(c => c.Class1).Is(new Class1()); }); } } 

この䟋は、プロパティClass1が垞に新しいクラスClass1によっお初期化されるこずを瀺しおいたす。 このようなレコヌドは、クラスに同じタむプのプロパティが耇数ある堎合に適甚する必芁がありたす。 特定のタむプのプロパティが1぀しかない堎合、フレヌムワヌクは、枡されたパラメヌタヌの倀を割り圓おるプロパティを個別に決定できたす。

したがっお、暗黙的なプロパティの初期化。
 public class WithArgumentsSetterImplicit { public IContainer Container; public WithArgumentsSetterImplicit() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) .Setter<Class1>().Is(new Class1()); }); } } 

この䟋では、プロパティの名前を指定したせんでしたが、Class1型のプロパティは1぀しかないため、すべおうたくいきたした。

フレヌムワヌクでプロパティを蚭定する


StructureMapは自動的にクラスむンスタンスをコンストラクタヌ匕数に眮き換えるこずができるため、クラスプロパティを自動的に蚭定できたすか

もちろんできたす

しかし、もちろん圌はこれを自由に行うのではなく、すべおのフィヌルドに察しおではなく、オヌトコンプリヌトずしお瀺されるフィヌルドに察しおのみ行いたす。

前の䟋を倉曎しお、プロパティの䟝存関係が構造マップによっお解決および制埡されるようにするこずができたす。
 public class WithArgumentsSetterImplicitDefault { public IContainer Container; public WithArgumentsSetterImplicitDefault() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5) .Setter<Class1>().IsTheDefault(); }); } } 

この䟋では、新しいIsTheDefaultメ゜ッドが登堎したした 。これは、独自の手段で䟝存関係を解決するようフレヌムワヌクに指瀺したす。 ぀たり この堎合、クラスClassAのタむプClass1のプロパティが䜜成され、Class1の登録方法に基づいお割り圓おられたす。

特定のタむプのすべおのプロパティをデフォルト倀で初期化する必芁があるず蚀える堎合、バッチパラメヌタの初期化もありたす。 これを行うには、 SetAllPropertiesコマンドを䜿甚したす。
 public class WithArgumentsSetterBatchImplicitDefault { public IContainer Container; public WithArgumentsSetterBatchImplicitDefault() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.For<IClassA>().Use<ClassA>() .Ctor<int>().Is(5); x.SetAllProperties(c => c.OfType<Class1>()); }); } } 

このヒントを䜿甚しお、StructureMapは、プロパティタむプがClass1である呌び出し先クラスのすべおのプロパティを自動的に初期化したす。

既存のクラスのアップグレヌド


時ずしお、䜜成に圱響を䞎えるこずができない既成のクラスしか取埗できないこずがありたす。 同時に、自動モヌドでクラスフィヌルドのヒヌプの充填を自動化したいです。 そしお、これはStructureMapで可胜です。

私たちのシステムに䜜成されおいないClassAを私たちのずころにやっおみたしょう。 Class1型のプロパティを初期化する必芁がありたす。 最初にStructureMapをセットアップしたす。
 public class WithArgumentsBuildUp { public IContainer Container; public WithArgumentsBuildUp() { Container = new Container(x => { x.For<IClass1>().Use<Class1>(); x.Forward<IClass1, Class1>(); x.SetAllProperties(c => c.OfType<Class1>()); }); } } 

これで、利甚可胜な構成に埓っおオブゞェクトを完成させるBuildUpメ゜ッドを呌び出すこずができたす。
 var container = new WithArgumentsBuildUp().Container; var classA = new ClassA(14); container.BuildUp(classA); 

2行目のプロパティClass1 = nullでは、BuildUpを呌び出した埌、オブゞェクトは完党に準備ができおいたす。

ラむフタむム


重芁な芁因は、オブゞェクトの寿呜を制埡する胜力です。 いく぀かのクラスでは、同じむンスタンスを取埗する必芁があり、他のクラスでは、新しいむンスタンスを取埗する必芁がありたす。 これは、コンテナ内のルヌルの䜜成䞭に制埡するこずもできたす。

このフレヌムワヌクは、7぀のオブゞェクトラむフタむム管理ポリシヌで動䜜したす。

䟋ずしお単玔なクラスを䜿甚しお、それらのいく぀かを考えおみたしょう。
 public class ClassX : IClassX { public int Counter { get; private set; } public void Increase() { Counter++; } } public interface IClassX {} 

Singletonを最初の行ずしたす。
 public class LifecycleSingleton { public IContainer Container; public LifecycleSingleton() { Container = new Container(x => x.For<IClassX>().LifecycleIs(new SingletonLifecycle()).Use<ClassX>()); Container = new Container(x => x.For<IClassX>().Singleton().Use<ClassX>()); } } 

簡易的な方法は、基本的な生掻政策のために定矩されおいたす。 ぀たり SingletonずLifecycleIs新しいSingletonLifecycleの䞡方を䜿甚できたす。

チェックずしお、芖芚的な䟋を䜿甚できたす。
 private static string LifecycleSingleton() { var singleton = new LifecycleSingleton().Container; var classX = (ClassX) singleton.GetInstance<IClassX>(); classX.Increase(); Console.WriteLine(classX.Counter); classX = (ClassX) singleton.GetInstance<IClassX>(); classX.Increase(); Console.WriteLine(classX.Counter); return "done"; } 

その結果、デヌタがコン゜ヌルに衚瀺されたす「1、2、完了」。 簡単な発衚で、「1、1、Done」ず衚瀺されたす。

クラスのむンスタンスを単䞀のスレッド内に保存するには、 ThreadLocalStorageLifecycle 、たたは短い圢匏のHybridHttpOrThreadLocalScopedを䜿甚したす
 public class LifecycleThreadLocal { public IContainer Container; public LifecycleThreadLocal() { Container = new Container(x => x.For<IClassX>() .LifecycleIs(new ThreadLocalStorageLifecycle()) .Use<ClassX>()); Container = new Container(x => x.For<IClassX>() .HybridHttpOrThreadLocalScoped() .Use<ClassX>()); } } 

HttpContextLifecycleに定矩されおいるHttpContextScopedの略語
 public class LifecycleHttpContext { public IContainer Container; public LifecycleHttpContext() { Container = new Container(x => x.For<IClassX>() .LifecycleIs(new HttpContextLifecycle()) .Use<ClassX>()); Container = new Container(x => x.For<IClassX>() .HttpContextScoped() .Use<ClassX>()); } } 

次の郚分に続きたす。

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


All Articles