Inject different classes that implement the same interface using Ninject

北城余情 提交于 2019-12-03 07:30:21

问题


I am implementing the builder design pattern to construct different kinds of graph objects to be displayed on a WPF UI. I am using Ninject as my IOC container. However, I am trying to find an elegant extendable solution.

I have a ChartDirector object that takes a IChartBuilder as a dependency. I also have TemperatureChartBuilder and ThresholdChartBuilder that implement IChartBuilder. I want to inject either TemperatureChartBuilder OR ThresholdChartBuilder to ChartDirector depending on an event that is fired or depending on a client call. I have illustrated my problem below in code.

// ChartDirector also depends on this
kernel.Bind<IExample>().To<Example>();

// when called in Method X...
kernel.Bind<IChartBuilder>().To<TemperatureChartBuilder>();

// when called in Method Y...
kernel.Bind<IChartBuilder>().To<ThresholdChartBuilder();

// TemperatureChartBuilder is a dependency of ChartDirector, need a way to dynamically
// allocate which binding to use.
var director = kernel.Get<ChartDirector>();

// without Ninject I would do
var director = new ChartDirector(new TemperatureChartBuilder);

// or
var director = new ChartDirector(new ThresholdChartBuilder);

EDIT:

Coupled with Gary's answer, and noting a slight edit that ChartDirector has another dependency, I now want to do something like this:

var director = kernel.Get<ChartDirector>().WithConstructorArgument(kernel.Get<IChartBuilder>("TemperatureChart"));

Is something like this possible?


回答1:


If you're just planning to use service location, as in your examples, then named bindings work fine, as per Garys answer.

A better approach, however, is to use constructor injection, and use attributes. For exampl, from the ninject wiki:

Bind<IWeapon>().To<Shuriken>().Named("Strong");
Bind<IWeapon>().To<Dagger>().Named("Weak"); 

...

class WeakAttack {
    readonly IWeapon _weapon;
    public([Named("Weak")] IWeapon weakWeapon)
        _weapon = weakWeapon;
    }
    public void Attack(string victim){
        Console.WriteLine(_weapon.Hit(victim));
    }
}

Based on your comment to Gary, you're (strangely enough) stumbling into territory similar to what I asked a question about a few hours ago. See Remo's answer here: Using WithConstructorArgument and creating bound type

You would use When condition to define when to create the correct instance.




回答2:


I would suggest using Contextual bindings (named bindings specifically) to accomplish this. That way you can do something like:

// called on app init
kernel.Bind<IChartBuilder>().To<TemperatureChartBuilder>().Named("TempChartBuilder");   
kernel.Bind<IChartBuilder>().To<ThresholdChartBuilder().Named("ThreshChartBuilder");

// method X/Y could both call method Z that grabs the correct chart director
var director = new ChartDirector(kernel.Get<IChartBuilder>("TempChartBuilder"));

Where "TempChartBuilder" could be a variable that tells ninject which binding to resolve. So rather binding on the fly you would resolve on the fly but all binding could be defined up front. Typically IOC containers are stored at the application domain level and only need to be defined once. There may be specific cases where you need to bind dynamically but those should be rare.

More info on contextual bindings: https://github.com/ninject/ninject/wiki/Contextual-Binding



来源:https://stackoverflow.com/questions/8585325/inject-different-classes-that-implement-the-same-interface-using-ninject

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!