Ninject in MVC5

I’ve found dozens of blogs and articles online about how to use Ninject in ASP.NET. It seems all of them are either a little outdated, or a little confusing.

So I put together a basic working application for reference. This uses Visual Studio 2017, with ASP.NET MVC 5 which will run in regular IIS.

I only know the very basics when it comes to Ninject, and I don’t even know if this is the best practice when building an app. But this does work, and does allow you to bind to different modules from different projects, which would allow you to do things like mocking database results or skipping error notifications.

If you have suggestions for how to improve this basic project (without complicating it too much), or any corrections or best practices I should consider, please let me know.

The basic idea is pretty straightforward:

  • Build interfaces and production implementations in separate projects.
  • Build a production module project which maps the two together.
  • In your live ASP.NET project, make a few tweaks to the MvcApplication class, including loading the production module.
  • Add parameters to your controller constructors, of the interface type. At runtime, they will be the real live type.
  • In other projects, like test projects, when you want to use different implementations of the interfaces, just build the new (or overriding) implementation classes, and a module that re-binds interfaces as appropriate.

Interfaces Project

// IRuleProcessor.cs

namespace HappyTarts.Services.Interfaces {
    public interface IRuleProcessor {
        string ProcessRule(string rule);
    }
}

Implementation Project

// RuleProcessor.cs

namespace HappyTarts.Services.Implementation {
    public class RuleProcessor : IRuleProcessor {
        string IRuleProcessor.ProcessRule(string rule) {
            return rule;
        }
    }
}

NinjectModules Project

// AppModule.cs

namespace HappyTarts.NinjectModules {
    public class AppModule : NinjectModule {
        public override void Load() {
            Bind<IRuleProcessor>().To<RuleProcessor>();
        }
    }
}

Web Project

// Global.asax.cs

namespace HappyTarts.Web {
    public class MvcApplication : NinjectHttpApplication {
        protected override void OnApplicationStarted() {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }

        protected override 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;
            }
        }

        private static void RegisterServices(IKernel kernel) {
            kernel.Load(new AppModule());
        }
    }
}

// HomeController.cs
namespace HappyTarts.Web.Controllers {
    public class HomeController : Controller {
        private readonly IRuleProcessor _ruleProcessor;

        public HomeController(IRuleProcessor ruleProcessor) {
            _ruleProcessor = ruleProcessor;
        }

        public ActionResult Index(string id) {
            ViewBag.Result = $"Result={_ruleProcessor.ProcessRule(id)}";
            return View();
        }
    }
}

Test Project

// UnitTests.cs

namespace HappyTarts.Tests {
    public class TestRuleProcessor : IRuleProcessor {
        string IRuleProcessor.ProcessRule(string rule) {
            return $"TEST {rule}";
        }
    }

    public class TestModule : AppModule {
        public override void Load() {
            base.Load();
            Rebind<IRuleProcessor>().To<TestRuleProcessor>();
        }
    }

    [TestClass]
    public class UnitTests {
        private IKernel _kernel;

        private void LoadTestModule() {
            _kernel = new StandardKernel(new TestModule());
        }

        private void LoadProdModule() {
            _kernel = new StandardKernel(new AppModule());
        }

        [TestMethod]
        public void TestTestModule() {
            LoadTestModule();
            var processor = _kernel.Get<IRuleProcessor>();
            Assert.AreEqual("TEST abc", processor.ProcessRule("abc"));
        }

        [TestMethod]
        public void TestProdModule() {
            LoadProdModule();
            var processor = _kernel.Get<IRuleProcessor>();
            Assert.AreEqual("abc", processor.ProcessRule("abc"));
        }
    }
}