Tuesday 18 October 2011

Dependency Injection for Filters with Ninject in MVC3 - Backward Compatibility

Tools used when writing this article: MvcScaffolding ver 1.0.0, Ninject ver 2.2.1.4 and Ninject.MVC3 ver 2.2.2.0.

This post will describe the third way to implement dependency injection for filters in MVC3.

For the preferred way to implement filter on actions or controllers using 'BindFilter' method, please see my previous post. The second way is also described on my other post.

In this post, we will see how to implement DI using 'Inject' attribute. This way of implementing is only meant for backward compatibility for MVC prior to version 3. For more information you can see https://github.com/ninject/ninject.web.mvc/wiki/Dependency-injection-for-filters and https://github.com/ninject/ninject.web.mvc/wiki/Injection-of-filter-attributes.

Setting up the DI for filter using this way is simpler, however we could not have a centralized location to specify where the filter applies. The reason is that we need to put the filter attribute on every controller(s) or action(s) where we want the filter to apply. So, if the filter is applied on lots of controllers or actions they could be harder to track.

First we prepare our filter class with a public service property that will be dependency injected. This property access modifier must be 'public', otherwise the DI will not work because the DI is going to be done through property injection. Then put '[Inject]' attribute notation on the property. When this notation is used, Ninject will automatically apply the injection to the property. Next, bind the service interface with its implementation class inside 'RegisterServices' method on 'NinjectMVC3.cs' file. Finally we can put the '[filterattribute]' notation on controller(s) or action(s) where we want the filter to apply.

Below are the complete codes:

Service interface and its implementation class:
public interface IGreetingService
{
    string GetGreeting();
}

public class GreetingServiceImplThree : IGreetingService
{
    public string GetGreeting()
    {
        return "Greeting - Backward Compatibility";
    }
}

Filter class:
public class GreetingFilterBackwardCompAttribute: ActionFilterAttribute
{
    [Inject]
    public IGreetingService _service { get; set; }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        ViewResult result = filterContext.Result as ViewResult;
        if (result != null)
        {
            result.ViewData["Greeting"] += _service.GetGreeting();
        }
    }
}

'RegisterServices' method on 'NinjectMVC3.cs':
private static void RegisterServices(IKernel kernel)
{
    . . .
    //bind the service interface with an implementation class
    kernel.Bind<IGreetingService>().To<GreetingServiceImplThree>();
    . . .
}   
Note here we just need to bind the service interface only, not the filter.

Usage:
[GreetingFilterBackwardComp]
public ViewResult Index()
{
    . . .
} 
To test, render 'ViewData["Greeting"]' somewhere on the view or shared view.

No comments: