適切な実装が存在しない状況では、 Ninject (またはその他の IoC コンテナー) を使用してデフォルトのバインディングを作成し、このデフォルトのバインディングを使用するのではなく、このデフォルトのバインディングを使用することは可能ですか
?ActivationException
複数のバインディングが存在する場合、または特定のリクエストに対してバインディングが存在しない場合は?
私は Ninject のFactoryおよびConventions拡張プロジェクトを使用してきましたが、それらが私がより基本的なレベルで犯している間違いを隠しているのではないかと考えているので、私がやりたいことを説明するためのテストを作成しました。私ができるように簡単に:
以下を考えると:
public interface IWidget { }
public class DefaultWidget : IWidget { }
public class BlueWidget : IWidget { }
そして、FluentAssertionsを使用した次のxUnitテスト:
[Fact]
public void Unknown_Type_Names_Resolve_To_A_Default_Type()
{
StandardKernel kernel = new StandardKernel();
// intention: resolve a `DefaultWidget` implementation whenever the
// 'name' parameter does not match the name of any other bound implementation
kernel.Bind<IWidget>().To<DefaultWidget>();
kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof(BlueWidget).Name);
kernel.Get<IWidget>("RedWidget").Should().BeOfType<DefaultWidget>();
// ACTIVATION EXCEPTION (**NO matching bindings available** for `IWidget`)
}
同様の質問に答えた人からの警告であるように思われることを除けば、この道をたどることが可能であるとしても、Service Locatorパターンの好ましくない実装につながるかどうかは正確にはわかりません.
それで、これは私が求めていることをするためのIoCコンテナ の悪用/誤用ですか?
私が持っているすべての明示的な型について、型を IoC でバインド/解決する必要があるように思われるので、その部分は私には間違っていないようです。実生活では、 のような明示的なバインディングがもっとたくさんあり、BlueWidget
入ってくる "RedWidget" 文字列値には未知の数のバリエーションがあります。
一般的に言えば、一部のインターフェイスのデフォルト実装の概念はそれほど珍しい状況ではないように思われますが、IoC コンテナーの領域内ではない場合、リクエストを解決するためのこのメカニズムはどこにありますか?
IWidget
また、ファクトリ パターンを使用して実装を作成する予定です。これまで、Ninject.Extensions.Factory によって自動的に作成されたファクトリをカスタム インスタンス プロバイダーとカスタム バインディング ジェネレーターで使用してきましたが、この問題を解決できません。
ファクトリの実装をより細かく制御できれば (つまり、Ninject.Extensions.Factory の自動ファクトリではなく、独自のファクトリを使用することで) 役に立ちますか? 以前は、アセンブリ リフレクションを使用して候補の型を見つけ、Activation.CreateInstance() を使用して、必要な特定の実装または既定の実装を作成しましたが、これらの実装に独自のコンストラクター注入依存関係があると、これは非常に面倒になります。依存性注入の原則がこれらの実装に適用されるためです。したがって、解決策として IoC コンテナーに移行しましたが、これは私が望んでいたようには機能しません。
更新 1 -- 独自の工場実装を使用した成功
の新しい実装を作成する必要があるたびに、IWidget
このファクトリを開いて更新する必要があるため、これにはあまり満足していません。ここでの私の例では、バインディングにも別の行を追加する必要がありますが、それが規則ベースのバインディングの出番であり、バインディング定義を常に更新する必要がないように使用する予定です。
このファクトリ実装を使用すると、
public interface IWidgetFactory { IWidget Create(string name); }
public class WidgetFactory : IWidgetFactory
{
private readonly IKernel kernel;
public WidgetFactory(IKernel kernel) { this.kernel = kernel; }
public IWidget Create(string name)
{
switch (name)
{
case "Blue":
return this.kernel.Get<IWidget>(typeof (BlueWidget).Name);
default:
return this.kernel.Get<IWidget>(typeof (DefaultWidget).Name);
}
}
}
このテストに合格することができます:
[Fact]
public void WidgetBuilders_And_Customizers_And_Bindings_Oh_My()
{
StandardKernel kernel = new StandardKernel();
kernel.Bind<IWidget>().To<DefaultWidget>().Named(typeof(DefaultWidget).Name);
kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof (BlueWidget).Name);
kernel.Bind<IWidgetFactory>().To<WidgetFactory>().InSingletonScope();
kernel.Get<IWidgetFactory>().Create("Blue")
.Should().BeOfType<BlueWidget>();
kernel.Get<IWidgetFactory>().Create("Red")
.Should().BeOfType<DefaultWidget>();
}
機能しますが、次の理由により、正しく感じられません。
- を注入する必要が
IKernel
ありますIWidgetFactory
- の新しい実装ごとに
IWidget
、IWidgetFactory
を更新する必要があります - このシナリオ用にNinject拡張機能がすでに作成されているはずです
アップデート 1 の終了
実装の数が多く、予想される「ウィジェット名」引数の範囲が本質的に無限であり、解決できないIWidget
ウィジェット名はすべてDefaultWidget
?
これ以上読む必要はありませんが、興味がある場合は、この問題に取り組む際に試したさまざまなテストを次に示します。
私が行ったテストの完全な進化は次のとおりです。
[Fact]
public void Unknown_Type_Names_Resolve_To_A_Default_Type()
{
StandardKernel kernel = new StandardKernel();
// evolution #1: simple as possible
// PASSES (as you would expect)
//kernel.Bind<IWidget>().To<BlueWidget>();
//kernel.Get<IWidget>().Should().BeOfType<BlueWidget>();
// evolution #2: make the only binding to a different IWidget
// FAILS (as you would expect)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Get<IWidget>().Should().BeOfType<BlueWidget>();
// evolution #3: add the binding to `BlueWidget` back
// ACTIVATION EXCEPTION (more than one binding for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>();
//kernel.Get<IWidget>().Should().BeOfType<BlueWidget>();
// evolution #4: make `BlueWidget` binding a *named binding*
// ACTIVATION EXCEPTION (more than one binding for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof (BlueWidget).Name);
//kernel.Get<IWidget>().Should().BeOfType<BlueWidget>();
// evolution #5: change `Get<>` request to specifiy widget name
// PASSES (yee-haw!)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof(BlueWidget).Name);
//kernel.Get<IWidget>("BlueWidget").Should().BeOfType<BlueWidget>();
// evolution #6: make `BlueWidget` binding *non-named*
// ACTIVATION EXCEPTION (**NO matching bindings available** for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>();
//kernel.Get<IWidget>("BlueWidget").Should().BeOfType<BlueWidget>();
// evolution #7: ask for non-existance `RedWidget`, hope for `DefaultWidget`
// ACTIVATION EXCEPTION (**NO matching bindings available** for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>();
//kernel.Get<IWidget>("RedWidget").Should().BeOfType<DefaultWidget>();
// evolution #8: make `BlueWidget` binding a *named binding* again
// ACTIVATION EXCEPTION (**NO matching bindings available** for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof(BlueWidget).Name);
//kernel.Get<IWidget>("RedWidget").Should().BeOfType<DefaultWidget>();
// evolution #9: remove `RedWidget` specification in Get<> request
// ACTIVATION EXCEPTION (back to **more than one** binding for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof(BlueWidget).Name);
//kernel.Get<IWidget>().Should().BeOfType<DefaultWidget>();
}