Partial injection with Ninject?

2019-07-30 01:41发布

I am trying to implement Ninject on an existing class that have a bool parameter in the constructor

public MyClass(bool val) //[OPTION 1: Current]
{
    //I Called static function to do something -> I want to inject this
    ...
    if(val){ ... } else{ ... }
    ...
}

I want to change the logic to inject ISomething.... the ctor will look like:

public MyClass(ISomething something, bool val) //[OPTION 2: Desired]
{
    something.DoSomething();
    ...
    if(val){ ... } else{ ... }
    ...
}

Also I used a disposable class that, called this one: [eg. used OPTION 1 -> trying to convert to OPTION 2]

public MyMaster(bool val)
{
    this.myclass = new MyClass(val); //-> now this has to be injected, how???
}

I implemented that a couple of times in a using context with true or false parameters, like this: [eg. used OPTION 1 -> trying to convert to OPTION 2]

using(var master = new MyMaster(true)){...} //-> This need to be refactored or injected, how???
//...
using(var master = new MyMaster(false)){...} //-> This need to be refactored or injected, how???

I am kind of blocked here... because I need to inject one value and not inject the other. Maybe implementing runtime injection? Also wrapping it in a using statement if it is needed?

NOTE: This is done in a class library that doesn't know anything about injection (Ninject is in later layers)

2条回答
【Aperson】
2楼-- · 2019-07-30 02:30

Generally, when you use the new keyword.. you're abandoning Dependency Injection.

I realise you said "This is done in a class library that doesn't know anything about injection (Ninject is in later layers)".. but your Composite Root should be just that.. the Root. What I mean by that, is that your DI bootstrapping should happen in the uppermost layer (generally off to the side, in a separate project that references all other projects).

Once you have that setup, you can then begin to think about using the Ninject.Factories extension. You could create a factory like this:

public interface ObjectCreatorFactory {
    MyClass CreateInstanceOfMyClass();
    MyMaster CreateInstanceOfMyMaster();
}

..and have that factory injected somewhere, thus giving you the ability to have Ninject instantiate the instances for you.

Your only other option is to revert to the good old Service Locator. I generally try and stay away from them.. since it makes Unit Testing difficult if your test implementations require the use of a DI Container.

Unfortunately these are your only options. There is no way to just hope Ninject does what it needs to do.. you need to tell it. That involves a change in how you're currently doing things.

查看更多
我命由我不由天
3楼-- · 2019-07-30 02:31

I hope I have understood the question correctly - you need a MyClass based on the bool passed to the MyMaster constructor, and you want to avoid using new but instead have Ninject supply it for you, is that right?

For this, you can use an abstract factory. (Note: You can, as Simon Whitehead pointed out, use the Ninject factory extensions for this but I will just do an explicit one here for clarity)

These interfaces/classes are defined in your class library, not in the composition root.

public interface IMyClassFactory
{
   IMyClass Create(bool val);
}

public interface IMyClass
{
   //whatever methods, etc, it's supposed to have.
}

public class MyClass:IMyClass
{
   public MyClass(bool val)
    {
       //do something with val here in the ctor
    }
}

This class is defined in your composition root

public class MyClassFactory:IMyClassFactory
{
   readonly IKernel _kernel;
   public MyClassFactory(IKernel kernel)
   {
      _kernel=kernel;
   }
   public IMyClass Create(bool val)
   {
      return _kernel.Get<IMyClass>(new ConstructorArgument("val",val);
   }
}

Then you bind the classes in your composition root, wherever you do your usual bindings:

kernel.Bind<IMyClassFactory>().To<MyClassFactory>();
kernel.Bind<IMyClass>().To<MyClass>();

and your MyMaster constructor now looks like:

readonly IMyClass myclass;
public MyMaster(bool val,IMyClassFactory factory)
{
    this.myclass = factory.Create(val); 
}

Once you get this working, and understand how it works, you can throw it away and use the Ninject Factory extensions as suggested, as they save you having to write the factory classes yourself :) (Example here)

查看更多
登录 后发表回答