samedi 26 août 2017

Using Ninject with static methods

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)

enter image description here

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?

Aucun commentaire:

Enregistrer un commentaire