Oct 21, 2009

Ninject 2.0: Passing Parameters

 image
I’m trying to learn (remember I’m a newbie) the most effective ways to handle parameters in Ninject 2.0. This wasn’t a result of refactoring an actual project, I was just curious.  I started out trying to bind a primitive type in a constructor. 

Parameters and Attributes

Let’s start by just binding a primitive type parameter. You can see below that the WorkoutRoom class takes a string in the constructor.
  1. public class RoomAttribute : Attribute { }
  2. public class WorkoutRoom
  3. {
  4.     public WorkoutRoom([Room] string roomName)
  5.     {
  6.         RoomName = roomName;
  7.     }
  8.  
  9.     public string RoomName { get; set; }
  10. }
You’ll notice that I created a custom attribute RoomAttribute and then used that attribute on the constructor’s parameter. Now I can leverage the contextual binding syntax we covered last time.
_kernel.Bind<string>().ToConstant(ROOM_NAME).WhenTargetHas<RoomAttribute>();
Where ROOM_NAME is just a const string I’ve defined with the default name I want this to have. Now whenever Ninject needs a WorkoutRoom it will get one with the default room name.

The ConstructorArgument 

If you don’t mind coupling the injection wiring to the actual names of parameters you can use ConstructorArguments.  Given the previous example I can rely on the fact that the parameter is named “roomName” and get rid of the attributes and use the following:
const string BIG_ROOM_NAME = "Room to move";
var room_arg = new ConstructorArgument("roomName",BIG_ROOM_NAME);
var my_room = _kernel.Get<WorkoutRoom>(room_arg);
Interestingly, you can combine these two techniques. Leave the attribute on the parameter and make the default name something politely informative:

const string ROOM_NAME = "Parameter name is no longer roomName - CHANGE IT BACK you dweeb!";
Now when your unit test runs and you check the room name
Assert.AreEqual(BIG_ROOM_NAME,my_room.RoomName);
The assertion will tell you everything you need to know about what happened.
Assert.AreEqual failed. Expected:<Room to move>. Actual:<Parameter name is no longer roomName - CHANGE IT BACK you dweeb!>.
You can easily pass more than one constructor argument. Given the following constructor
[Inject]
public Barracks(string theNickName, string theFormalName)
{
    NickName = theNickName;
    FormalName = theFormalName;
}
You can test it with
[TestMethod]
public void TestSimpleParameter2()
{
    const string NICK_NAME = "Dojo Sweet Dojo";
    var nick_name_arg = new ConstructorArgument("theNickName", NICK_NAME);

    const string FORMAL_NAME = "SweatShop";
    var formal_name_arg = new ConstructorArgument("theFormalName", FORMAL_NAME);

    var my_barracks = _kernel.Get<Barracks>(nick_name_arg,formal_name_arg);

    Assert.AreEqual(NICK_NAME, my_barracks.NickName);
    Assert.AreEqual(FORMAL_NAME, my_barracks.FormalName);
    Assert.AreEqual(ROOM_NAME, my_barracks.Room.RoomName);
}
You aren’t limited to primitive types with these techniques. Here’s an example taken from Ninject’s unit tests.
Where the barracks class has a constructor that looks like
public Barracks( IWarrior warrior )
{
    Warrior = warrior;
}
You can use a ConstructorArgument like
var constructorArgument = new ConstructorArgument("warrior", new Samurai(new Sword()));
var barracks = kernel.Get<Barracks>(constructorArgument);
Previous Related Posts
  1. Contextual Binding
  2. Becoming a Ninject Warrior

About Me

My photo
Tod Gentille (@todgentille) is now a Curriculum Director for Pluralsight. He's been programming professionally since well before you were born and was a software consultant for most of his career. He's also a father, husband, drummer, and windsurfer. He wants to be a guitar player but he just hasn't got the chops for it.