At the moment I am refactoring an old project and trying to implement dependency injection with Ninject.
I came across a more or less simple problem. I found a working solution for this, but i am not sure if this is the best way to solve this.
I try to explain the situation as precisely as possible:
I have an interface ITool
:
public interface ITool
{
string Caption { get; set; }
string Name { get; }
IAction[] Actions { get; }
}
There is also an abstract class AbstractTool
(the concrete content is not relevant for the question)
Further I have the derived class GenericTool
:
public class GenericTool : AbstractTool
{
private readonly string furtherInformation;
public override string FurtherInformation
{
get { return furtherInformation; }
}
public GenericTool (string furtherInformation, string caption, string name, Action[] actions)
: base(caption, name, actions)
{
this.furtherInformation= furtherInformation;
}
}
At one point this GenericTool
is used and instantiate a couple of times like this:
new OtherObject(
{
new List<GenericTool>
{
new GenericTool("info1","caption1","name1", new IActions[]
{ new Action1(), new Action2()}),
new GenericTool("info2","caption2","name2", new IActions[]
{ new Action3(), new Action4()}),
...
}
}...
I wanted to resolve this with only one call:
kernel.Get<OtherObject>();
But I wasn't sure how to bind the different actions (Action1, 2, 3, 4) that they will resolved correctly to the belonging GenericTool
.
My solution was to create a derived class of GenericTool
and used the NamedAttribute
of Ninject
public class SpecialTool1 : GenericTool
{
public SpecialTool1([Named("SpecialTool1Action")] IAction[] actions)
: base("info1", "caption1","name1", actions)
{}
}
public class SpecialTool2 : GenericTool
{
public SpecialTool2([Named("SpecialTool2Action")] IAction[] actions)
: base("info2", "caption2","name2", actions)
{}
}
The binding for the actions :
Bind<IAction>.To<Action1>().Named("SpecialTool1Action");
Bind<IAction>.To<Action2>().Named("SpecialTool1Action");
Bind<IAction>.To<Action3>().Named("SpecialTool2Action");
Bind<IAction>.To<Action4>().Named("SpecialTool2Action");
With this I get the required result. But I have to create a lot of small classes like SpecialTool1
.
So my second work around this:
Bind<IAction>.To<Action1>().Named("SpecialTool1Action");
Bind<IAction>.To<Action2>().Named("SpecialTool1Action");
Bind<ITool>().To<GenericTool>()
.WithConstructorArgument("furtherInformation","info1")
.WithConstructorArgument("caption", "caption1")
.WithConstructorArgument("name", "name1")
.WithConstructorArgument("actions", GetLateNamedConstructorArgument);
The function GetLateNamedConstructorArgument
:
private IAction[] GetLateNamedConstructorArgument(IContext context)
{
IEnumerable<IAction> actions= context.Kernel.
GetAll<IAction>("SpecialTool1Action");
return actions.ToArray();
}
With the last solution I got the same result without creating dozens of classes.
But is there a better way to solve this? Can I declare the name for the required bindings in a other way? Is there maybe a complete other work around?
EDIT
I imagine something like this:
Bind<ITool>().To<GenericTool>()
...
.WithConstructorArgument("actions", [Named("SpecialTool1Action")]);
And Ninject injects all actions with the SpecialTool1Action
name.