同じタイプの複数のマッピングが登録されている状況は、登録されているすべてのタイプのサービス(つまり、
IService[]
)を転送する必要がある場合に監視し
IService[]
ます。 しかし、コンテナから特定のサービスを取得する必要がある場合はどうでしょうか? これを行うために、Unityはオブジェクトに名前を付ける機能を提供します。 たとえば、このコードのアナログを実装するには
「登録済み」マッピングのみを登録する必要があります。var svc10 = new MyService(10);<br/>
var svc15 = new MyService(15);<br/>
また、すべての登録済みマッピングを取得することもできます。 これは、var uc = new UnityContainer();<br/>
//
uc.RegisterType<IService, MyService>( "ten" ,<br/>
new InjectionConstructor( new InjectionParameter(10)))<br/>
.RegisterType<IService, MyService>( "fifteen" ,<br/>
new InjectionConstructor( new InjectionParameter(15)));<br/>
//
var _10 = uc.Resolve<IService>( "ten" );<br/>
var _15 = uc.Resolve<IService>( "fifteen" );<br/>
ResolveAll()
関数を使用して行われます。
foreach (IService svc in uc.ResolveAll<IService>() )<br/>
svc.DoSomething();<br/>
外部作成
DIを実行したいオブジェクトがコントロール外で作成される場合があります。 これが頻繁に発生する例は、WCF、WPF、さまざまなリモート処理/ SoAスクリプトです。 オブジェクトは他の誰かによって作成されたという事実にもかかわらず、全体のプロセスにオブジェクト(および依存オブジェクト)を接続できることは依然として素晴らしいことです。
別の合成例を次に示します。
オブジェクトの準備がすでに完了しているため、DIメカニズムは適用されませんでしたが、これは問題ではありませんpublic class HelperClass<br/>
{<br/>
public void DoSomething()<br/>
{<br/>
Console.WriteLine( "Doing something" );<br/>
}<br/>
}<br/>
public class SomeService<br/>
{<br/>
[Dependency]<br/>
public HelperClass MyHelperClass { get; set; }<br/>
public void Go() { MyHelperClass.DoSomething(); }<br/>
}<br/>
<br/>
⋮<br/>
<br/>
var uc = new UnityContainer();<br/>
var ss = Activator.CreateInstance<SomeService>();<br/>
Console.WriteLine(ss.MyHelperClass == null ); // True
BuildUp()
を使用して事後的に取得できます。
コードからわかるように、var uc = new UnityContainer();<br/>
// ""
var ss = Activator.CreateInstance<SomeService>();<br/>
Console.WriteLine(ss.MyHelperClass == null ); // True
var ss2 = uc.BuildUp<SomeService>(ss) ;<br/>
Console.WriteLine(ss2.MyHelperClass == null ); // False
ss2.Go();<br/>
Console.WriteLine(ReferenceEquals(ss, ss2)); // True
BuildUp()
メソッドは、オブジェクトがIoCコンテナーを生成した場合に作成される依存関係を既存のオブジェクトに「構築」します。 この例では、上部構造が作成されない場合、新しいオブジェクトは作成されず、古いオブジェクトが使用されることも示しています。
もちろん、オブジェクトが外部で作成された場合、
Resolve()
メソッドの動作は変わります。 特に、サービスが
IService
実装している場合、
IService
から既存のオブジェクトにマップし、問題なく同じコピーを発行できます。 これを行うには、
RegisterInstance()
メソッドを使用して既存のオブジェクトを
RegisterInstance()
ます。
var uc = new UnityContainer();<br/>
var ss = Activator.CreateInstance<SomeService>();<br/>
uc.BuildUp<SomeService>(ss);<br/>
// IService ss
uc.RegisterInstance<IService>(ss); <br/>
var svc = uc.Resolve<IService>();<br/>
svc.Go();<br/>
テスト中
単体テストを簡素化するのは、DIメカニズムの目標からです。 一番下の行は、ほとんどの開発者にとって、ユニットはクラスであるため、テスト内の他のクラスを置き換える必要があるということです。 別の簡単な例を見てみましょう。
次のテストは統合です。なぜなら、public interface IAdder<br/>
{<br/>
int Add( int first, int second);<br/>
}<br/>
public sealed class AdderService : IAdder<br/>
{<br/>
public int Add( int first, int second)<br/>
{<br/>
return first + second;<br/>
}<br/>
}<br/>
public class MyApp<br/>
{<br/>
private IAdder adder;<br/>
public MyApp(IAdder adder)<br/>
{<br/>
this .adder = adder;<br/>
}<br/>
public int AddAndMultiply( int first, int second, int third)<br/>
{<br/>
return adder.Add(first, second) * third;<br/>
}<br/>
}<br/>
AdderService
と
MyApp
2つのクラスの特定のコピーが含まれ
MyApp
。
ところで、作業コードでは、コンテナは上記のとおりに正確に構成されます。 ただし、テスト時には、[Test]<br/>
public void NotAUnitTest()<br/>
{<br/>
var uc = new UnityContainer();<br/>
uc.RegisterType<IAdder, AdderService>();<br/>
Assert.AreEqual(20, uc.Resolve<MyApp>().AddAndMultiply(2, 3, 4));<br/>
}<br/>
AdderService
別の「テスト」実装に置き換えることができます。 最も簡単なオプションは、単に定数を返す
FakeAdderService
を記述することです。
今、私たちはコンテナを変更します-ほら! -完全なユニットテストがあります。public class FakeAdderService : IAdder<br/>
{<br/>
public int Add( int first, int second)<br/>
{<br/>
return 5; // !
}<br/>
}<br/>
もちろん、私たちの偽物は特に柔軟性があります-結局、入力時に期待する値を正確に返します(つまり、テストでは「2、3」と書き込み、偽物では数値5を返します)。 そして、突然別のテストで他の値を使用する必要がある場合、どうすればよいですか? 別の偽オブジェクトを作成しますか? または、偽のパラメーター化可能にし、コンテナーでパラメーターを構成しますか?[Test]<br/>
public void UnitTestWithFake()<br/>
{<br/>
var uc = new UnityContainer();<br/>
uc.RegisterType<IAdder, FakeAdderService >();<br/>
Assert.AreEqual(20, uc.Resolve<MyApp>().AddAndMultiply(2, 3, 4));<br/>
}<br/>
幸いなことに、この種のものは何も必要ありません。 代わりに、Rhino MocksやTypemock Isolatorなどのモックフレームワークを使用できます。 その後、偽の
IService
オブジェクトが自動的に挿入されるように、独自の偽のクラスなしでテストを書き換えることができます。これにより、必要な値が正確に返されます。
上記では、Typemockを使用して偽オブジェクトを作成し(これについてはこちらで確認できます )、var uc = new UnityContainer();<br/>
var svc = Isolate.Fake.Instance<AdderService>();<br/>
Isolate.WhenCalled(() => svc.Add(0, 0)).WillReturn(5);<br/>
uc.RegisterInstance<IAdder>(svc);<br/>
Assert.AreEqual(20, uc.Resolve<MyApp>().AddAndMultiply(2, 3, 4));<br/>
Add()
メソッドを呼び出す動作を設定し(常に5を返します)、コンテナーに追加しました。 したがって、私は本格的な、簡単に構成可能な単体テストを受け取りました。
今のところすべてです。 Automockingとその他の興味深いことについては次回議論します。