Intro
Dependency Injection, DI in short, also known as Inversion-of-Control, is one of the core principles of SOLID programming and one of my favorite techniques to write software. It is a software design pattern that allow us to develop loosely coupled code, thus reducing the coupling between various software components.
It is important to remember that a major facet of object-oriented programming is “loose coupling”. This means that objects should have the minimum number of dependencies they need in order to do their job. An object’s dependencies should be on interfaces as opposed to “concrete” objects (A concrete object is an object created by the ‘new’ keyword).
With loose coupling, you enable easier maintainability and greater reusability. Moreover, you can feature “mock” objects designed to take the place of costly services.
There are three types of dependency injection:
- Constructor Injection
- Setter Injection
- Method Injection
In this post, I’ll talk about the first one, the constructor injection, so let’s begin!
Step 1 – Preparations
In order to use dependency injection, you need to make sure the class you want to be injected is implementing an interface.
example:
public interface IApplicationLayer { };
public class ApplicationLayer : IApplicationLayer
Step 2 – Adding the Ninject to the solution
Install the needed packages from nuget
- Ninject
- Ninject.MVC5
- Ninject.Web.Common
- Ninject.Web.Common.WebHost
Step 3 – Verify NinjectWebCommon class
After you add the needed Ninject packages from NuGet, the install script should generate a class called “NinjectWebCommon
” under App_Start
folder.
The generated class should look something like this:
public static class NinjectWebCommon { private static readonly Bootstrapper bootstrapper = new Bootstrapper(); /// <summary> /// Starts the application /// </summary> public static void Start() { DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule)); DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule)); bootstrapper.Initialize(CreateKernel); } /// <summary> /// Stops the application. /// </summary> public static void Stop() { bootstrapper.ShutDown(); } /// <summary> /// Creates the kernel that will manage your application. /// </summary> /// <returns>The created kernel.</returns> private static IKernel CreateKernel() { var kernel = new StandardKernel(); try { kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel); kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>(); RegisterServices(kernel); return kernel; } catch { kernel.Dispose(); throw; } } /// <summary> /// Load your modules or register your services here! /// </summary> /// <param name="kernel">The kernel.</param> private static void RegisterServices(IKernel kernel) { } }
Step 4 – Creating the DI layer
Create a class derives from NinjectModule
, which will define the binding between the contracts (Interfaces) and the class that implements them. Since this class actually does the binding, I’d use it inject any configurations to the implemented classes constructors.
As can be seen in the example below, I am using a dictionary to hold configuration values that I loaded earlier. I fetch the needed value I want to inject from the dictionary, then provide it to the binding using the WithConstructorArgument
extension.
public class SolutionModule : NinjectModule { string someConfigurationValue ; public SolutionModule(Dictionary Config) { Config.TryGetValue("SomeConfigurationValue", out someConfigurationValue ); } public override void Load() { // Bind the ApplicationLayer class to the IApplicationLayer interface. // ApplicationLayer class must implement IApplicationLayer interface Bind<IApplicationLayer>(). To<ApplicationLayer>(). WithConstructorArgument("injectedConfigurationValue", someConfigurationValue); } }
Attention: Make sure the parameter name that we are injecting values into in the constructor matches the one you are using with WithConstructorArgument
(case sensitive)
Step 5 – Load the DI modules
The RegisterServices method is where we should be loading any configuration values that needs to be injected to the implementing classes constructors.
private static void RegisterServices(IKernel kernel) { string SomeConfigurationValue = ConfigurationManager.AppSettings.Get("SomeSettingInAppConfig"); Dictionary<string, string> Config = new Dictionary<string,string>(); Config.Add("SomeConfigurationValue", SomeConfigurationValue); // You can add as many module as you need var modules = new List { new SolutionModule(Config) }; kernel.Load(modules); }
And that’s it!, now every time you use IApplication interface, during runtime, the DI library (in our case Ninject) will instantiate and inject the proper object.
Appendix – Setting up the Log4Net Ninject module
If you are using Log4Net to do the logging for you, here’s a quick setup I grabbed from the web:
public class LoggingModule : NinjectModule { public override void Load() { Bind<log4net.ILog>().ToMethod(x => log4net.LogManager.GetLogger(GetParentTypeName(x))) .InSingletonScope(); Bind<ILocalLogger>().To<LocalLogger>() .InSingletonScope(); } private string GetParentTypeName(IContext context) { //return context.Request.ParentContext.Request.ParentContext.Request.Service.FullName; return context.Request.ParentRequest.ParentRequest.Target.Member.DeclaringType.ToString(); } }