Ninject vs. MEF for ASP.NET MVC Apps

April 23, 2010 in ASP.NET MVC

This week I have been looking at using a dependency injection container for an MVC 2 application.  Since I am using .NET Framework 4 with Managed Extensibility Framework (MEF) built in, it is one option to consider.  I have also heard good things about Ninject.  So lets do a quick comparison between Ninject and MEF with the simplest of implementations.

Ninject

To start off, I downloaded the .NET Framework 3.5 release of Ninject assembly and then reference that as I recompile the Ninject.Web.MVC assembly for .NET 4. Then I created a new ASP.NET MVC 2 project and reference those assemblies:

image

Next I modified the MvcApplication class in Global.asax.cs to inherit from NinjectHttpApplication and use the OnApplicationStarted and CreateKernal overrides to wire Ninject


public class MvcApplication : NinjectHttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );

    }

    protected override void OnApplicationStarted()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);

        RegisterAllControllersIn(Assembly.GetExecutingAssembly());
    }

    protected override IKernel CreateKernel()
    {
        var modules = new INinjectModule[]
            {
                new WebModule()
            };

        return new StandardKernel(modules);
    }

 

To associate an interface to an implementation, I create a WebModule class that inherits from NinjectModule:


public class WebModule : NinjectModule
{
    public override void Load()
    {
        Bind<IHelloService>()
            .To<HelloService>();
    }
}

 

The service and interface are very basic:


public interface IHelloService
{
    string GetMessage();
}

public class HelloService : IHelloService
{
    public string GetMessage()
    {
        return "Hello there.";
    }
}

 

Since the Controllers are now wired to use items bound in the module, we can use the interface as a parameter to the constructor or set a property using the [Inject] attribute:

Constructor Injection


public class HomeController : Controller
{
    public IHelloService HelloService { get; set; }

    //Constructor injection
    public HomeController(IHelloService service)
    {
        this.HelloService = service;
    }

    public ActionResult Index()
    {
        ViewData["Message"] = HelloService.GetMessage();

        return View();
    }
}

 

Property Injection


public class HomeController : Controller
{
    //Property injection
    [Inject]
    public IHelloService HelloService { get; set; }

    public ActionResult Index()
    {
        ViewData["Message"] = HelloService.GetMessage();

        return View();
    }
}

So, how would we accomplish the same thing in MEF?

MEF

To make the comparison easier, I created MefHttpApplication and MefControllerFactory classes.

First, add a reference to System.ComponentModel.Composition as that is where MEF lives:

image 

To get similar functionality that Ninject has in its NinjectHttpApplication, I created a MefHttpApplication:


public abstract class MefHttpApplication : HttpApplication
{
    public void Application_Start()
    {
        var catalog = CreateCatalog();
        var container = new CompositionContainer(catalog);

        ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));

        OnApplicationStarted();
    }

    protected virtual void OnApplicationStarted() {}

    protected abstract ComposablePartCatalog CreateCatalog();
}

When the application starts, the derived class gets the specify catalog used to gather composition information. The MefHttpApplication then creates a CompositionContainer based on that catalog, overrides MVC’s default ControllerFactory, and provides an OnApplicationStarted method for the derived class:


public class MvcApplication : MefHttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );

    }

    protected override void OnApplicationStarted()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
    }

    protected override ComposablePartCatalog CreateCatalog()
    {
        return new AssemblyCatalog(Assembly.GetExecutingAssembly());
    }
}

Comparing this to the NinjectHttpApplication-derived class, there is no need to register the controllers separately or to create a WebModule to define the mapping between IHelloService and HelloService.  This will be accomplished in a different way.  By creating an AssemblyCatalog for the currently executing assembly, all classes will be scanned to see if there are any Exports to register.  In our case, we have one Export on the HelloService:


public interface IHelloService
{
    string GetMessage();
}

[Export(typeof(IHelloService))]
public class HelloService : IHelloService
{
    public string GetMessage()
    {
        return "Hello there.";
    }
}

In MEF, we need to remember the triad: Export, Import, Compose.  We already have our Export and the corresponding Import can be found in the Controller on the HelloService property:


public class HomeController : Controller
{
     [Import]
     public IHelloService HelloService { get; set; }

     public ActionResult Index()
     {
         ViewData["Message"] = HelloService.GetMessage();

         return View();
     }

     public ActionResult About()
     {
         return View();
     }
}

 

It is also possible to import via a constructor parameter by decorating the constructor with the ImportingConstructor attribute but this would require more work in the MefControllerFactory class:


public class MefControllerFactory : DefaultControllerFactory
{
    private CompositionContainer container;

    public MefControllerFactory(CompositionContainer container)
    {
        this.container = container;
    }

    public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        IController controller = base.CreateController(requestContext, controllerName);

        container.ComposeParts(controller);

        return controller;
    }
}

In the MefHttpApplication we replaced the default ControllerFactory with the MefControllerFactory which is derived from DefaultControllerFactory.  Every time MVC creates a new Controller, it goes through this class which simply calls base to create the Controller.  Just before returning the Controller, the container.ComposeParts method is called passing in the controller which allows any Imports defined in the Controller to be satisfied by any Exports found in the catalog.  All three steps of composition have been completed.

So Which Should I Use?

As always, that depends on your specific situation.  If all you need is injection into your controllers, then Ninject is simple way to get that done.  If you don’t want to rely on a 3rd-party solution, don’t mind creating your own HttpApplication and ControllerFactory classes, or you want to use MEF in other scenarios then MEF might be a better choice.  One example is to use MEF to define a Controller in a separate plug-in.

In this example, we compared MEF to Ninject, but a similar comparison could be done for Unity, StructureMap, Castle Windor, or other.

Ninject vs. MEF for ASP.NET MVC Apps

2 Comments

    1. [...] to Vote[FriendFeed] Ninject vs. MEF for ASP.NET MVC Apps (4/23/2010)Friday, April 23, 2010 from [...]

    2. Thanks Mark, Really interesting post and a nice comparison. I’ve only recently found ninject after playing with structuremap and the others. I liked it and had used the example in Rob Cons tekpub video to get me started but having seen this MEF looks like it could be a nice think to look into. Great post.

    3. Bret Ferrier says:

      So what about the performance of the one versus the other. I like how MEF is “baked in” but it would be cool to see performance measures as well.

Leave a Reply

Ninject vs. MEF for ASP.NET MVC Apps

1 Trackback