Friday, June 26, 2009

Dependency Injections - NInject

There are many ways of injecting dependencies, you can use Spring.NET, StructureMap etc. NInject is one way of achieving this. It encourages relying on code for dependency rather than depending on XML which always prone to human errors (http://ninject.org/). All you need to do is to configure NInject e.g. Bind<>().To<>();. In the following example I have implemented Inversion of Control with minimal changes using NInject:

Problem: Lets take an application that prints user info. The printed output needs to be different (text file, PDF etc.). It depends on how external party implements printing.

Solution:
Define a printer Interface :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PrintInterface
{
public interface IPrintDriver
{
void Print(string Name, string Age, string Address, string Phone);
}
}


Write an implementation, say "Implementation1" doing nothing:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PrintInterface;
namespace PrintImplementation1
{
public class PDFPrinter : IPrintDriver
{
public void Print(string Name, string Age, string Address, string Phone)
{
//Do Nothing
}
}
}


Now bind the interface with implementation. I will include Ninject.Core library and extend StandardModule:


using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Core;
using PrintInterface;
using PrintImplementation2;
namespace DICustomModule
{
public class CustomModule : StandardModule
{
public override void Load()
{
Bind&ltiprintdriver>().To<> ();
}
}
}

Let's create the main application. First create a simple class, pass IPrinterDriver as an argument in parametrized constructor. Call print method of the interface and pass User Info:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PrintInterface;

namespace PrintMyInfo
{
class Print
{
private IPrintDriver _prnDrv;

public Print(IPrintDriver prnDrv)
{

_prnDrv = prnDrv;

}

public void PrintInfo()
{

_prnDrv.Print("John", "25", "123 Fake Street, Dallas", "xxx xxx xxxx");
}
}
}

Create a simple window forms, include Ninject.Core namespace, create an instance of CustomModule (bind IPrintDriver is to actual object). Create a kernel object of
type IKernel by passing CustomModule, get the handle and call its Print method:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DICustomModule;
using Ninject.Core;

namespace PrintMyInfo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
CustomModule module = new CustomModule();

IKernel kernel = new StandardKernel(module);

Print printMyInfo = kernel.Get();

printMyInfo.PrintInfo();
}
}
}


//This is entry point
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace PrintMyInfo
{
static class Program
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}


Now I need to print the output to text file (not PDF). Create another implementation of IPrintDriver:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using PrintInterface;

namespace PrintImplementation2
{
public class TextPrinter : IPrintDriver
{
public void Print(string Name, string Age, string Address, string Phone)
{
string printText=null;
printText = Name + ", " + Age + ", " + Address + ", " + Phone;
TextWriter tw = new StreamWriter("Print.txt");

// write a line of text to the file
tw.WriteLine(printText);

// close the stream
tw.Close();
}

}
}


Update the CustomModule and attach new object to IPrintDriver:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Core;
using PrintInterface;
using PrintImplementation2;

namespace DICustomModule
{
public class CustomModule : StandardModule

{
public override void Load()
{

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

}

}
}


All I needed to do was to update the intermediate layer (CustomModule). Main program doesn't need to be recompiled.

Isn't a simple approach!

No comments:

Post a Comment