web-dev-qa-db-ja.com

Ninjectを使用してコンストラクターに追加パラメーターを指定してインスタンスを作成する

私はNinjectの使用を開始し、問題に直面することにしました。次のシナリオがあるとします。 IServiceインターフェイスと、このインターフェイスを実装する2つのクラスがあります。また、IServiceとintを取得するコンストラクターを持つクラスもあります。 Ninjectでこのクラスのインスタンスを作成するにはどうすればよいですか(このintをハードワイヤーしたくない、インスタンスを取得するたびに渡したい)?

状況を示すコードを次に示します。

interface IService
{
    void Func();
}

class StandardService : IService
{
    public void Func()
    {
        Console.WriteLine("Standard");
    }
}

class AlternativeService : IService
{
    public void Func()
    {
        Console.WriteLine("Alternative");
    }
}


class MyClass
{
    public MyClass(IService service, int i)
    {
        this.service = service;
    }

    public void Func()
    {
        service.Func();
    }

    IService service = null;
}
class Program
{
    static void Main(string[] args)
    {
        IKernel kernel = new StandardKernel(new InlineModule(
            x => x.Bind<IService>().To<AlternativeService>(),
            x => x.Bind<MyClass>().ToSelf()));

        IService service = kernel.Get<IService>();

        MyClass m = kernel.Get<MyClass>();
        m.Func();
    }
}
63
StuffHappens

_With.ConstructorArgument_は、この目的のために1.0に存在していました。 2.0では、構文がわずかに変更されました:- With.Parameters.ConstructorArgument with ninject 2.

コンテキスト、プロバイダー、引数を使用してこのようなものをより正確に渡す方法の詳細と例については、 挿入された依存関係に値を挿入する を参照してください。

編集:スティーブンは私のコメントが無関係であるふりをすることを選択したので、いくつかの例(2.0の場合)で私が言っていることを明確にしたい:

_MyClass m = kernel.Get<MyClass>( new ConstructorArgument( "i", 2) );
_

私の目には非常に明確で、何が起こっているのかを正確に述べています。

よりグローバルな方法でパラメーターを決定できる位置にいる場合は、プロバイダーを登録して次のように実行できます。

_class MyClassProvider : SimpleProvider<MyClass>
{
    protected override MyClass CreateInstance( IContext context )
    {
        return new MyClass( context.Kernel.Get<IService>(), CalculateINow() );
    }
}
_

そして、次のように登録します:

_x => x.Bind<MyClass>().ToProvider( new MyClassProvider() )
_

NB CalculateINow()ビットは、最初の答えのようにロジックを配置する場所です。

または、次のように複雑にします。

_class MyClassProviderCustom : SimpleProvider<MyClass>
{
    readonly Func<int> _calculateINow;
    public MyClassProviderCustom( Func<int> calculateINow )
    {
        _calculateINow = calculateINow;
    }

    protected override MyClass CreateInstance( IContext context )
    {
        return new MyClass( context.Kernel.Get<IService>(), _calculateINow() );
    }
}
_

次のように登録します:

_x => x.Bind<MyClass>().ToProvider( new MyClassProviderCustom( (  ) => new Random( ).Next( 9 ) ) )
_

更新:上記よりも定型化されていない、はるかに改善されたパターンを示す新しいメカニズムは、_Ninject.Extensions.Factory_拡張に組み込まれています。以下を参照してください: https://github.com/ninject/ninject.extensions.factory/wiki

前に述べたように、 毎回異なるパラメーターを渡す必要があり、依存関係グラフに複数のレベルがある場合、このようなことをする必要があるかもしれません

最後の考慮事項は、_Using<Behavior>_を指定していないため、カーネルのオプション(サンプルではTransientBehavior)で指定/デフォルト設定されているデフォルトにデフォルトを設定することです。ファクトリーがiを即座に計算すること[例:オブジェクトがキャッシュされていた場合]

ここで、FUDされ、光沢が付けられているコメントの他のいくつかのポイントを明確にします。 Ninjectであれ、DIであれ、DIの使用について考慮すべき重要な点:

  1. 可能な限りコンストラクターの注入を行うようにして、コンテナー固有の属性やトリックを使用する必要がないようにします。 あなたのIoCコンテナが表示されている と呼ばれるブログ記事があります。

  2. コンテナに行くコードを最小化し、何かを要求します-それ以外の場合、コードはa)特定のコンテナ(CSLが最小化できる)b)プロジェクト全体のレイアウト方法に結合されます。 CSLがあなたが思っていることをしていないことを示す良いブログ投稿があります。この一般的なトピックは サービスの場所と依存関係の挿入 と呼ばれます。更新:詳細かつ完全な根拠については、 http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx を参照してください。

  3. 静的およびシングルトンの使用を最小限に抑える

  4. [グローバル]コンテナが1つだけであり、ニースのグローバル変数のように必要なときに要求するだけでよいと想定しないでください。複数のモジュールとBind.ToProvider()を正しく使用すると、これを管理する構造が得られます。そうすれば、それぞれのサブシステムが独立して動作し、トップレベルのコンポーネントなどに結び付けられた低レベルのコンポーネントを持つことはありません。

誰かが私が言及しているブログへのリンクを埋めたいと思うなら、私は感謝します(SOのすべての他の投稿からすでにリンクされているので、これはすべてです誤解を招く答えの混乱を避けることを目的として導入されたばかりの複製UI。)

今、Joelだけが入って来て、ニースの構文および/またはこれを行う正しい方法について本当に私をまっすぐに設定できたら!

更新:この回答は、獲得した賛成票の数から明らかに有用ですが、次の推奨事項を作成したいと思います。

  • 上記は少し時代遅れであり、正直に言うと、 .netのDependency Injection を読んでからほとんど恥ずかしいと思う多くの不完全な思考を反映しています-今すぐ実行して購入します-それはDIだけではありません半分は、ここで依存性注入タグをぶらぶらして時間を費やしすぎた人からの、それを取り巻くすべてのアーキテクチャの懸念の完全な処理です。
  • 読んでください Mark Seemannの最高評価の投稿はこちらSO今すぐ -あなたはすべてから貴重なテクニックを学びます
93
Ruben Bartelink