My goal is to use Ninject in my application with static methods. I am aware of the fact that the static methods should not have any dependencies since it is hard from testing point of view. But, in my application I am not in the position to make each method static to non-static. I need to implement DI with minimal changes, i.e. I should change the code at one lance and it should change the implementation everywhere where that method or class is being called. I don't want to use the Service Locator pattern too, considering that is not even a pure DI pattern.
In order to keep all those above points in my mind, I implemented the following set up and it has been working well. I need your thoughts whether I did it correct or not to use Ninject with static methods.
My ASP.NET WebForms set up:
Solution: NinjectPlayGround
Projects:
NinjectPlayGround (Web Project)
NinjectPlayGround.BL (Business Layer)
NinjectPlayGround.Common (POCO layer)
In order to use Ninject I installed Ninject.Web
NinjectWebCommon.cs Code:
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(NinjectPlayGround.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(NinjectPlayGround.App_Start.NinjectWebCommon), "Stop")]
namespace NinjectPlayGround.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
using NinjectPlayGround.Common;
using NinjectPlayGround.BL;
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)
{
kernel.Bind<ILogger>().To<DefaultLogger>();
// Set static properties
CommonHelper.Logger = kernel.Get<ILogger>();
}
}
}
I added the following two custom lines there:
kernel.Bind<ILogger>().To<DefaultLogger>();
// Set static properties
CommonHelper.Logger = kernel.Get<ILogger>();
CommonHelper is the class which has static methods where I want to use Logger instance.
CommonHelper class code:
using NinjectPlayGround.Common;
namespace NinjectPlayGround.BL
{
public class CommonHelper
{
public static ILogger Logger { get; set; }
public static void RunCleanUp()
{
// Run clean up
//.....
Logger.Log("Clean up done");
}
}
}
ILogger class code:
namespace NinjectPlayGround.Common
{
public interface ILogger
{
void Log(string message);
}
}
DefaultLogger class code:
using System.Diagnostics;
namespace NinjectPlayGround.Common
{
public class DefaultLogger : ILogger
{
public void Log(string message)
{
Debug.WriteLine(message);
}
}
}
Since Ninject attribute is not allowed on static properties like this:
public static ILogger Logger { get; set; }
I set that property via RegisterServices method present in NinjectWebCommon class which is called only once per application start (correct me if I am wrong here)
// Set static properties
CommonHelper.Logger = kernel.Get<ILogger>();
From UI this helper method is called like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Ninject;
using NinjectPlayGround.BL;
namespace NinjectPlayGround
{
public partial class _default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
CommonHelper.RunCleanUp();
}
}
}
Al the above setup is working fine i.e if I change the DefaultLogger to PersistentLogger (which is another implementation of ILogger)
kernel.Bind<ILogger>().To<DefaultLogger>();
to
kernel.Bind<ILogger>().To<PersistentLogger>();
I can see PersistentLogger being called instead of DefaultLogger without making any mess/changes in the CommonHelper class.
Is this the correct way to use Ninject with static methods?
Is this how I need to set all static properties which I need to set via DI in RegisterServices method of NinjectWebCommon class?
Will I face any difficulties with this kind of implementation?