web-dev-qa-db-ja.com

DIとコンストラクターパラメーターを組み合わせる?

コンストラクターインジェクションを「手動」コンストラクターパラメーターと組み合わせるにはどうすればよいですか?すなわち。

public class SomeObject
{
    public SomeObject(IService service, float someValue)
    {
    }
}

IServiceは、DIコンテナーによって解決/注入される必要があり、someValueが指定される必要があります。 2つをどのように混合しますか?

44
George R

このような構造は、可能な限り回避する必要があります。したがって、自問してみてください。このパラメーターは本当にコンストラクター引数として必要ですか?または、オブジェクトで実行するメソッドにパラメーターを渡すことにより、SomeObjectを、それに依存するすべての人が再利用するステートレスなものに置き換えることができますか?

例えばの代わりに

public class SomeObject
{
    private float someValue
    public SomeObject(IService service, float someValue)
    {
        this.someValue = someValue
    }

    public float Do(float x)
    {
        return this.Service.Get(this.someValue) * x;
    }
}

使用する

public class SomeObject
{
    public SomeObject(IService service)
    {
    }

    public float Do(float x, float someValue)
    {
        return this.Service.Get(someValue) * x;
    }
}

必要な場合は、工場に行きます。

public interface ISomeObjectFactory
{
    ISomeObject CreateSomeObject(float someValue);
}

public class SomeObjectFactory : ISomeObjectFactory
{
    private IKernel kernel;
    public SomeObjectFactory(IKernel kernel) 
    {
        this.Kernel = kernel;
    }

    public ISomeObject Create(float someValue)
    {
        return this.kernel.Get<ISomeObject>(WithConstructorArgument("someValue", someValue);
    }
}

プレビュー:Ninject 2.4はもう実装を必要としませんが、許可します

kernel.Bind<ISomeObjectFactory>().ToFactory();  // or maybe .AsFactory();
38
Remo Gloor

別のアプローチ-2つのステップでの初期化(Ninject関連ではなく、DIフレームワーク):

public class SomeObject
{
    private readonly IService _service;

    public SomeObject(IService service)
    {
        // constructor only captures dependencies
        _service = service;
    }

    public SomeObject Load(float someValue)
    {
        // real initialization goes here
        // ....

        // you can make this method return no value
        // but this makes it more convienient to use
        return this;
    }
}

および使用法:

public static class TestClass
{
    public static void TestMethod(IService service)
    {
        //var someObject = new SomeObject(service, 5f);
        var someObject = new SomeObject(service).Load(5f);
    }
}
4
Arek

あなたは本当にDIを使用しようとするべきではありません。このため。あらゆる種類の奇抜なソリューションを考え出すことができますが、それらは将来的には意味をなさない場合があります。

私たちのアプローチは、D.I。を介してファクトリを作成することであり、ファクトリのCreateメソッドは、渡されたD.I.容器。このパターンを頻繁に使用する必要はありませんが、実際に使用すると、製品がよりクリーンになります(依存関係グラフが小さくなるため)。

3
Beep beep

'somevalue'が常に定数である場合、以下の投稿で説明されているように、コンテナーに型を登録しているときにInjectionParametersを使用することを考えることができます

ここを参照

しかし、そうでない場合は、インスタンスの解決中にパラメーター値を指定する方法がないため、「someValue」をコンストラクターから移動し、それをクラスのプロパティにすることを検討できます。

1
TalentTuner

これにタグを付けたNInjectでは、 この投稿で説明されているFuncModule を使用して、Func<parameters you wish to feed in,T>の形式で自動生成されたファクトリを注入します。

このアプローチは、autofacでも利用できます。

さまざまな ファクトリーメソッドアプローチはこの質問への回答でカバーされています

編集:NBこれは面白いかもしれませんが、@ Remo Gloorのソリューションを使用してください(そしてこの性質のソリューションを回避するためのアドバイスが重要です)

1
Ruben Bartelink

私はおそらくこれに素朴な解決策を使うでしょう。 someValueの値がわかっている場合は、それを必要に応じてコンストラクタから削除し、someValueを設定できるようにオブジェクトにプロパティを追加します。このようにして、コンテナーからオブジェクトを取得し、オブジェクトがあるときに値を設定できます。

私の他の提案は、直接アクセスする代わりに、そのようなオブジェクトの作成に使用できるファクトリを作成することです。次に、コンテナにファクトリを登録し、そのファクトリを使用してインスタンスを作成します。このようなもの:

public class SomeObjectFactory : ISomeObjectFactory
{
    private IYourService _service;
    public SomeObjectFactory(IYourService service) 
    {
        _service = service;
    }

    public ISomeObject Create(float someValue)
    {
        return new SomeObject(_service, someValue);
    }
}

そのようなパターンを試すことができます。

PDATE:改善コメントを反映するようにコードを更新しました。

1
Tomas Jansson

これが良い方法かどうかはわかりませんが、別の方法で解決できる可能性があります。パラメーターのインターフェイスを作成する場合は、必要な値(またはどこかから取得する値)を使用してインターフェイスを実装するクラスを作成します。このようにして、DIはこれらのパラメーターでも機能します。

interface ISomeParameters
{
  public float SomeValue { get; set; }
}

class SomeParameters : ISomeParameters
{
 public float SomeValue{ get; set; } = 42.0;
}

services.AddSingleton(ISomeParameters, SomeParameters)

public MyService(IService service, ISomeParameters someParameters)
{
  someParameters.SomeValue
 ...
0
AndersK