Convention-based IOC is great … if everyone follows the conventions

Recently I started using Ninject for Inversion Of Control (IOC) and Dependency Injection (DI) on an ASP.Net MVC3 project.  Ninject is a very neat framework and is easy to use BTW and its offers some slick MVC3 integration through the Ninject.Web.Mvc project.

Being a proponent of convention-over-configuration, I also looked for some auto-registration code for Ninject and soon found the Ninject.Extensions.Conventions project.  The default convention here also suits me, since it binds IFooService to FooService in transient scope. Using the Scan extension method, I could now have the following code to initialise my IOC:

kernel.Scan(scanner =>
{
    scanner.FromAssemblyContaining<ProjectRepository>();
    scanner.BindWith<DefaultBindingGenerator>();
});

So far, so good, I thought, and now to apply IOC to my WCF services, which are hosted within the MVC web project (important point!).  Once again, Ninject has an answer, in the form of the Ninject.Extensions.Wcf project.  This project offers a custom ServiceHostFactory that will apply IOC to your service implementation simply by adding the Factory attribute in your .svc file:

<%@ ServiceHost Language="C#" 
                Debug="true" 
                Service="SharpFellows.ScrumToolkit.Web.Services.Authentication" 
                CodeBehind="Authentication.svc.cs" 
                Factory="Ninject.Extensions.Wcf.NinjectServiceHostFactory" %>

Very nice … except that it didn’t work (yet).  This didn’t surprise me much at this stage because both Ninject.Web.Mvc and Ninject.Extensions.Wcf define a custom HttpApplication class and both libraries expect you to derive from this in your global.asax.cs class.  I opted to keep the class for Mvc and patch in the functionality required by the class for Wcf.  [As an aside, this is only possible because of the nature of open source software – hurray for open source]  So here was my first attempt:

kernel.Scan(scanner =>
{
    scanner.FromAssemblyContaining<ProjectRepository>();
    // Ninject.Extensions.Wcf uses some binding internally
    scanner.FromAssemblyContaining<NinjectServiceHostFactory>();
    scanner.BindWith<DefaultBindingGenerator>();
});
// Ninject.Extensions.Wcf expects to find the kernel here
KernelContainer.Kernel = kernel;

Looking good … except that it still didn’t work.  Requests to the WCF service were now all met with this:

The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.

After much head-scratching, it turns out that the IOC binding done internally by the Ninject.Extensions.Wcf project is to bind ServiceHost to NinjectServiceHost and this binding does not follow the default conventions laid down by Ninject.Extensions.Convention!  Obvious really, once you understand the conventions and look at the required binding.  So now the fix was pretty simple:

// Conventions pick up most of our IOC bindings  :-)
kernel.Scan(scanner =>
{
    scanner.FromAssemblyContaining<ProjectRepository>();
    scanner.BindWith<DefaultBindingGenerator>();
});

// This binding doesn't follow the convention  :-/
kernel.Bind<ServiceHost>().To<NinjectServiceHost>();

// Ninject.Extensions.Wcf expects to find the kernel here
KernelContainer.Kernel = kernel;

And now everything worked smoothly.  Smile

The moral of the story?  It is possible to get MVC controllers and WCF service classes controlled by the same Ninject kernel.  And if you use conventions, don’t assume that all libraries can and do use them!

June 2 2011
Older Posts