Ryan LanciauxNew Media Mercenary

Real Ultimate Power : Dependency Injection with Ninject

March 26, 2008 by ryan
Ninja
UPDATE: Nate Kohari (the author of Ninject) posted some excellent information in the comments (enough info that it could probably be a post on its own).

Recently Scott Hanselman compiled a list of Dependency Injection frameworks for .NET. I really didn't plan on trying anything new but Ninject really jumped out at me (honestly, it was more the reference to ninjas than anything). After seeing a couple examples, I thought I would check it out in a bit more detail.

Disclaimer: I've been playing around with Ninject for all of about 3 hours now ... it's very possible there's a better way to do some of this stuff :) So I would really appreciate any feedback.

On to the code

Okay, for this very contrived example we're going to be building car objects out of just an engine and a drive type (extremely accurate I know). Just like in my StructureMap example, I'm going to start by creating the interfaces followed by a default class that we're going to use as our skeleton car ... the interfaces are pretty basic so no need to spend too much time on them.

    public interface IDriveType

    {

        string DriveType{ get;}

    }

 

    public interface IEngine

    {

        string EngineType { get; }

    }



Both of the interfaces have only one string property that will be used for output. A little on the basic side but hey, we're looking at IoC not an accurate car construction. Next we're going to add all our implementations of the Engine

    class FourCylinder : IEngine

    {

        public string EngineType

        {

            get { return "4-Cylinder"; }

        }

    }

 

    class Rotary : IEngine

    {

        public string EngineType

        {

            get { return "Rotary"; }

        }

    }

 

    class SixCylinder : IEngine

    {

        public string EngineType

        {

            get { return "6-Cylinder"; }

        }

    }



And the drive type implementations...

    public class RWD : IDriveType

    {

        public string DriveType

        {

            get { return "Rear Wheel Drive"; }

        }

    }

 

    class FourWD : IDriveType

    {

        public string DriveType

        {

            get { return "Four Wheel Drive"; }

        }

    }

 

    class FWD : IDriveType

    {

        public string DriveType

        {

            get { return "Front wheel drive"; }

        }

    }



And finally the class we're going to use as our base car.

    public class BaseAuto

    {

        private IDriveType _driveType;

        private IEngine _engine;

 

        public string DriveType

        {

            get { return _driveType.DriveType; }

        }

 

        public string Engine

        {

            get { return _engine.EngineType; }

        }

 

        [Inject]

        public BaseAuto(IDriveType drive, IEngine engine)

        {

            _driveType = drive;

            _engine = engine;

        }

    }



You'll notice the [Inject] attribute above our constructor. This is basically telling Ninject to toss in an implementation of the IDriveType and IEngine interfaces to this constructor (more on  Constructor Injection over here).

Now we're on to the fun stuff. Ninject does not use XML to configure injections. Instead we're going to use a class called Module to define all that. As the documentation says, this class should implement IModule but thankfully (at least for the sake of testing), there's a pre-defined base implementation called StandardModule that we can extend.

    public class BaseModule : StandardModule

    {

        public override void Load()

        {

            Bind<IEngine>().To<Rotary>();

            Bind<IDriveType>().To<RWD>();

        }

    }



In the module definition, we're basically saying when we request an object from the Kernel (more on that in a sec.) we want the Rotary class in place of IEngine and the RWD instead of IDriveType. Simple enough, now lets take a look at the Kernel definition / initial code (I'm using winforms for the sake of example but you can really go w/ whatever project type you'd like). A lot of thought went into the naming of the form.

    public partial class Form1 : Form

    {

        private BaseAuto _rx8;

 

        public Form1()

        {

            InitializeComponent();

 

            IKernel kernel = new StandardKernel(new BaseModule());

 

            _rx8 = new BaseAuto(

                kernel.Get<Auto.IDriveType>(),

                kernel.Get<Auto.IEngine>()

                );

 

            MessageBox.Show("RX8: \n Drive Type: " + _rx8.DriveType +

                "\n Engine:" + _rx8.Engine);

        }

    }



And when we run it...



Just what we expected! Lets make things a little more interesting ... Say we wanted to add some other cars to our application? We probably don't want everything to be a rear-wheel drive rotary (Unless you really like RX-8's -- in that case you can buy my RX8 *shameless plug*). Anyways, we're going to accomplish this by changing up our Module a bit to bind to a different IEngine / IDriveType implementation depending on the context.

    public class BaseModule : StandardModule

    {

        public override void Load()

        {

            Bind<IEngine>().To<Rotary>()

                .Only(When.Context.Variable("carType").EqualTo("RX8"));           

            Bind<IDriveType>().To<RWD>()

                .Only(When.Context.Variable("carType").EqualTo("RX8"));

 

            Bind<IEngine>().To<SixCylinder>()

                .Only(When.Context.Variable("carType").EqualTo("Jetta"));

            Bind<IDriveType>().To<FWD>()

                .Only(When.Context.Variable("carType").EqualTo("Jetta"));

 

            Bind<IEngine>().To<FourCylinder>()

                .Only(When.Context.Variable("carType").EqualTo("WRX"));

            Bind<IDriveType>().To<FourWD>()

                .Only(When.Context.Variable("carType").EqualTo("WRX"));

        }

    }



Not too bad right? I mean, just looking at the code we can pretty much tell what's going on due to the Fluent Interface goodness. We can perform more powerful comparisons on our context variable but for now, this will work. Next we need to set up our context in the core part of our application
(where we're instantiating and requesting classes from our Kernel).

        public Form1()

        {

            InitializeComponent();

 

 

            IKernel kernel = new StandardKernel(new BaseModule());

 

            _rx8 = new BaseAuto(

                    kernel.Get<Auto.IDriveType>(

                    With.Parameters.ContextVariable("carType", "RX8")

                ),

                kernel.Get<Auto.IEngine>(

                    With.Parameters.ContextVariable("carType", "RX8")

                )

            );

 

            _jetta = new BaseAuto(

                    kernel.Get<Auto.IDriveType>(

                    With.Parameters.ContextVariable("carType", "Jetta")

                ),

                kernel.Get<Auto.IEngine>(

                    With.Parameters.ContextVariable("carType", "Jetta")

                )

            );

 

 

            _wrx = new BaseAuto(

                    kernel.Get<Auto.IDriveType>(

                    With.Parameters.ContextVariable("carType", "WRX")

                ),

                kernel.Get<Auto.IEngine>(

                    With.Parameters.ContextVariable("carType", "WRX")

                )

            );

 

        }



I've added some buttons so we don't get spammed with message boxes on form load... if we fire this off and click on the various buttons, we see we're getting the expected results!


All in all, I'm liking Ninject for more than just the name -- I can definitely see myself using this in small to medium sized apps. Please let me know what you think...

More info on Ninject:


kick it on DotNetKicks.com




Very Quick and Simple Dependency Injection with StructureMap

February 26, 2008 by ryan

There are a lot of resources on the web about dependency injection and using StructureMap, however, I wanted to write something that was an extremely simple example. This is basically the tip of the iceberg but hopefully it will help someone. 

We want to make our application very loosely coupled -- to achieve this 'loose coupling' we're going to have several projects in the solution. What this means if we need to change any part of this application later on (we wouldn't want to in this case since its a demo and all), we could do so without impacting everything else. Anyways, we're going to create three class libraries and a WinForms application.

Next we want to create our main inteface -- this will be under the DisplayMessage Project:

 

namespace DisplayMessage

{

    public interface IDisplayMessage

    {

        string message();

    }

}

The interface defines just one method that, when implemented, will return a string stating what class its coming from. Next, we want to create our two implementation classes (one under Implementation1, the other under Implementation2). Please keep in mind I'm not suggesting to have every class in it's own library -- it's just for the sake of example :)

Implementation1:

    public class MessageOne : IDisplayMessage

    {

        public string message()

        {

            return "This is a message from Implementation1";

        }

    }

 Implementation2:

    public class MessageTwo : IDisplayMessage

    {

        public string message()

        {

            return "This is a message from Implementation2";

        }

    }

Okay that was easy enough, now on to the Forms App.  We're first going to add a reference to StructureMap and the project DisplayMessage and create a file called StructureMap.config -- this config file is going to define all of our assemblies. We want to make sure we edit the properties of this file and set the Copy to Output Directory option to "Copy Always." StructureMap will use this file at runtime to get our object references. The config file looks like this: 

<?xml version="1.0" encoding="utf-8" ?>

<StructureMap>

  <Assembly Name="DisplayMessage" />

  <Assembly Name="Implementation1" />

  <Assembly Name="Implementation2" />

 

  <PluginFamily Type="DisplayMessage.IDisplayMessage"

                Assembly="DisplayMessage"

                DefaultKey="MessageOne">

    <Plugin Type="Implementation1.MessageOne"

            Assembly="Implementation1"         

            ConcreteKey="MessageOne" />

    <Plugin Type="Implementation2.MessageTwo"

                Assembly="Implementation2"

                ConcreteKey="MessageTwo" />   

  </PluginFamily>

</StructureMap>

Notice we define a PluginFamily for the IDisplayMessage interface and set the default implementation to be MessageOne (the DefaultKey of PluginFamily references the ConcreteKey of the Plugin). Other than that, this should be pretty straight-forward but if you have any confusion, please check out the StructureMap documentation. Only a couple more things to do before we can run this...

Ok, we're going to add 3 buttons to our form -- one for the default IDisplayMessage and one for each implementation.

 Now to add the code...

        //Default IDisplayMessage 

        private void btnDefault_Click(object sender, EventArgs e)

        {

            IDisplayMessage msg = StructureMap.ObjectFactory.GetInstance<IDisplayMessage>();

            System.Windows.Forms.MessageBox.Show(msg.message());

        }

 

        //Implementation1

        private void btnOne_Click(object sender, EventArgs e)

        {

            IDisplayMessage msg = StructureMap.ObjectFactory.GetNamedInstance<IDisplayMessage>("MessageOne");

            System.Windows.Forms.MessageBox.Show(msg.message());

        }

 

        //Implementation2

        private void btnTwo_Click(object sender, EventArgs e)

        {

            IDisplayMessage msg = StructureMap.ObjectFactory.GetNamedInstance<IDisplayMessage>("MessageTwo");

            System.Windows.Forms.MessageBox.Show(msg.message());

        } 

Lets parse this up a little bit...

  • IDisplayMessage msg = StructureMap.ObjectFactory.GetInstance<IDisplayMessage>();

    This statement gets the default IDisplayMessage object in the StructureMap.config file. Currently, it will get the same object as getting a named instance of "MessageOne"
  • IDisplayMessage msg = StructureMap.ObjectFactory.GetNamedInstance<IDisplayMessage>("MessageOne");

    This statement gets the object associated with the ConcreteKey "MessageOne"
  • IDisplayMessage msg = StructureMap.ObjectFactory.GetNamedInstance<IDisplayMessage>("MessageTwo");

    This statement gets the object associated with the ConcreteKey "MessageTwo"

Instead of simply hitting F5, we will need to build the application -- we want to copy the DLL files from Implementation1 and Implementation2 to the bin directory of the forms app and run the executable there.  For testing, however, we can add a reference to both projects (this completely defeats the purpose of dependency injection so be sure to remove the references later on) or adjust the output directory of the implementation class libraries to be the same as the Form application's bin directory. Running the application shows that everything is working as expected.

For more information please check out the following links:

kick it on DotNetKicks.com







© 2008 Ryan Lanciaux :: powered by BlogEngine.NET