Dependency Injection and Interception with Ninject and MVC3

by rcats on December 24, 2010

MVC really helps in creating clean code and seperation of logic vs layout. However, MVC is not going to help you keep your controllers clean of implementation details; you’ll need some form of  a factory, inversion of control or dependency injection. Like Ninject.

Ninject is a clean library that helps you do exactly that (and much more as you’ll find out). No endless config files here, just some strongly typed Ninject power.

Using Ninject with MVC3

For each type of project (WebForms, WCF, MVC) you’ll have to download and reference the appropriate Ninject extension in your project. You can find them on the Ninject website, or you can use the library package manager (NuGet extension) to install Ninject.MVC3, without having to leave your IDE!

After installing a file called AppStart_NinjectMVC3.cs is added to your project. The code in there is used to wire up Ninject during the MVC startup routine. This is where the Ninject Kernel gets initialized. I’m doing it using a Factory here to make sure a single instance gets created:

public static class AppStart_NinjectMVC3
{
    public static void RegisterServices(IKernel kernel) 
    {
        kernel.Bind<IRepository>().To<Repo>();
    }
 
    public static void Start() {
        // Create Ninject DI Kernel
        IKernel kernel = NinjectFactory.GetNinjectFactory();
        // Register services with our Ninject DI Container
        RegisterServices(kernel);
 
        // Tell ASP.NET MVC 3 to use our Ninject DI Container
        DependencyResolver.SetResolver(new NinjectServiceLocator(kernel));
    }
}
 
public class NinjectFactory
{
    public static IKernel _kernel = null;
    public static IKernel GetNinjectFactory()
    {
        if (_kernel == null)
        {
            INinjectModule mod = new CustomModule();
            _kernel = new StandardKernel(mod);
        }
        return _kernel;
    }
}

U can use the RegisterServices method to add bindings or add them using a NinjectModule. This is just basic Ninject constructor binding, where we bind each reference of IRepository to the Repo implementation. The beauty is ofcourse that you can change this to use a different implementation without altering any code, which is perfect for testing, but you can ofcourse easily do stuff like this:

#if DEBUG
    Bind<IRepository>().To<DebugRepo>().WhenInjectedInto<SomeClass>();
 
#else
    Bind<IRepository>().To<Repo>().WhenInjectedInto<SomeClass>();
#endif

Now we have our basic dependency injection going, let’s start interception. We have to download and add the interception libraries, as they are not available in the library package manager yet.

To start using interception, first we add a new class to our project, and add the following code snippet:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Ninject.Extensions.Interception;
using Ninject.Extensions.Interception.Infrastructure.Language;
namespace InterceptionTest
{
    public class InterceptAllModule : InterceptionModule
    {
        public override void Load()
        {
            <strong>Kernel.Intercept(p => (true)).With(new TimingInterceptor());</strong>
        }
    }
}

You may have noticed that besides the plumbing, there’s just one line of code in there. The predicate we pass on to the Intercept (extension) method can be anything, but this one just selects all. TimingInterceptor is a class that uses a stopwatch to measure the execution time (taken from the ninject demo source code).

public class TimingInterceptor : SimpleInterceptor
{
    readonly Stopwatch _stopwatch = new Stopwatch();
    protected override void BeforeInvoke(IInvocation invocation)
    {
        _stopwatch.Start();
    }
 
    protected override void AfterInvoke(IInvocation invocation)
    {
        _stopwatch.Stop();
        string message = string.Format("[Execution of {0} took {1}.]",
        invocation.Request.Method,
        _stopwatch.Elapsed);
        HttpContext.Current.Response.Write(message + @"<br \>");
        _stopwatch.Reset();
    }
}

Now all we have to do is add a call to the “InterceptAllModule”. Below we change the factory to accomplish this:

    _kernel = new StandardKernel(mod, new InterceptAllModule() );

The timing interceptor will now be invoked on every method call. In the example I’m writing the results to the browser, but it makes sense write this to a logfile using log4net or the ninject logging extension.

What I haven’t mentioned yet is that the Ninject Interception extension uses a 3rd party library and there are 2 to choose from: Linfu and DynamicProxy2. The dll’s come with the download, just make sure you add the reference in your project. Also, I ran into an issue using Linfu that made me switch to DynamicProxy2. I blogged about it here: http://rinzecats.com/2010/12/interception-with-ninject-linfu-vs-dynamicproxy2/.

Hope this helps,

Rinze

Tell Everyone:
  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • Reddit

One comment

Thank you, very useful. Well explained !

by Marc F on April 10, 2011 at 09:07. Reply #

Leave your comment

Not published.

If you have one.