7

適切な実装が存在しない状況では、 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>();
}

機能しますが、次の理由により、正しく感じられません。

  1. を注入する必要がIKernelありますIWidgetFactory
  2. の新しい実装ごとにIWidgetIWidgetFactoryを更新する必要があります
  3. このシナリオ用に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>();
}
4

1 に答える 1

2

これがどこに積み重なるかはわかりませんが、機能します。ただし、名前付きパラメーターを渡すことはできません。空のパラメーターでのみ機能します。言い換えれば、これを行うことはできません:

var stuff1 = kernel.Get<IWidget>("OrangeWidget");

OrangeWidget が存在しない場合。デフォルトを取得するには、次のようにする必要があります。

var stuff2 = kernel.Get<IWidget>();

次に例を示します。

IDictionary dic = new Dictionary<string, string>();

dic.Add("BlueWidget", "BlueWidget");
dic.Add("RedWidget", "RedWidget");

kernel.Bind<IWidget>().To<DefaultWidget>()
    .When(x => x.Service.Name != (string)dic[x.Service.Name]); 
kernel.Bind<IWidget>().To<BlueWidget>().Named("BlueWidget");

var stuff1 = kernel.Get<IWidget>("BlueWidget");
var stuff2 = kernel.Get<IWidget>();

これは、あなたが興味を持っているかもしれないクールな Ninject の投稿です...

名前付きパラメーターについて何か追加したいと思います。これは、このドキュメントに基づいています。Named パラメータを使用すると、バインディングを Get<> するときに名前で呼び出すことができますが、「デフォルト」にはなりません。したがって、そのバインディングを取得するには、実際には DefaultWidget という名前を渡す必要があります。これは機能します:

kernel.Bind<IWidget>().To<BlueWidget>().Named("BlueWidget");
kernel.Bind<IWidget>().To<DefaultWidget>().Named("DefaultWidget");

var blueOne = kernel.Get<IWidget>("BlueWidget");
var defaultOne = kernel.Get<IWidget>("DefaultWidget");

誰かがデフォルトを実装する方法を理解できるなら、私はそれを必要としたことはありませんが、その方法を知りたいと思います. 私は Ninject の学生で、とても気に入っています。

アップデート:

わかった。ここでクールなソリューションを見つけました。

Ninject を拡張するクラスを作成しました。

public static class NinjectExtensions
{
    public static T GetDefault<T>(this IKernel kernel)
    {
        return kernel.Get<T>(m => m.Name == null);
    }

    public static T GetNamedOrDefault<T>(this IKernel kernel, string name)
    {
        T result = kernel.TryGet<T>(name);

        if (result != null)
            return result;

        return kernel.GetDefault<T>();
    }
}

これがあなたの Unknown_Type メソッドです...

public static void Unknown_Type_Names_Resolve_To_A_Default_Type()
{
    StandardKernel kernel = new StandardKernel();

    IDictionary dic = new Dictionary<string, string>();

    dic.Add("BlueWidget", "BlueWidget");
    dic.Add("RedWidget", "RedWidget");

    kernel.Bind<IWidget>().To<DefaultWidget>().When(x => x.Service.Name != (string)dic[x.Service.Name]);
    kernel.Bind<IWidget>().To<BlueWidget>().Named("BlueWidget");

    // this works!
    var sup = kernel.GetNamedOrDefault<IWidget>("Not here");

    var stuff1 = kernel.Get<IWidget>("BlueWidget");
    var stuff2 = kernel.Get<IWidget>();
}
于 2013-09-10T16:16:57.347 に答える