Wrapping the Ninject Kernel with ServiceLocator

I was a big fan of using Unity or Mef for IoC in my apps, we used Prism heavily in one of my past projects with Silverlight. Prism offered a IoC with your choice of Unity or Mef for your container as well as very good approach to building a scalable application with the notion of Prism Modules.

I recently came on board to a project extensively using Ninject for our MVC3 application. Ninject has the notion of a Kernel which is pretty much how you access your container of registered or bound instances.

In the event where you would ever change out or switch your IoC implementation or choice of frameworks, a nice wrapper, that wrapped your method of accessing your container would be great. You can in theory switch out your IoC (container) with Mef, Unity, Sprint.NET, AutoFac, Castle Windsor, or any other IoC framework with minimal refactoring (here’s a great list of some popular IoC frameworks from Scott Hanselman). 

With a little research I found that Ninject has a ServiceLocator adapter that you can wire up when you setup your Kernel. With this wired up you can access your Ninject container with the standard Microsoft ServiceLocator, which is also a widely used pattern.

For this demonstration we are just going to use the MVC3 application used from my previous blog: http://blog.longle.net/2012/02/15/inject-ioc-my-mvc3-application-in-less-than-5-minutes/

  1. Right click your references folder and click “Manage NuGet Packages”
  2. Search for ServiceLocator, and install the CommonServiceLocator.NinjectAdapter Package
  3. You should now see the NinjectAdapter reference added
  4. Let’s wire this up now, we will visit the Global.asax.cs file where we setup and initialized our Kernel (container), here’s where we an register the NinjectServiceLocator Adapter with Microsoft.Practices.ServiceLocation.ServiceLocator interface.
    
            protected void Application_Start()
            {
                RegisterMyDependencyResolver();            
                AreaRegistration.RegisterAllAreas();
                RegisterGlobalFilters(GlobalFilters.Filters);
                RegisterRoutes(RouteTable.Routes);
            }
    
            private void RegisterMyDependencyResolver()
            {
                var standardKernel = new StandardKernel();
                standardKernel.Bind<IHelloWorldService>().To<HelloWorldService>();
                ServiceLocator.SetLocatorProvider(() => new NinjectServiceLocator(standardKernel));
                DependencyResolver.SetResolver(new MyDependencyResolver(standardKernel));
            }
    
    
  5. So the first place I wanted to use the ServiceLocator was in our MyDependendencyResolver class, where the MVC runitme routes all request get instances for all your registered or bound interfaces. What happens here is that when the MVC runtime requests for a registered instance of a specific interface and your container does not have one, MVC expects a your IoC container to return a null value and at this point MVC will fallback and attempt to return the default instance.

    For example if the MVC runtime requests for an IControllerFactory and you didn’t setup a binding or registration for this interface the MVC runtime will just return a DefaultControllerFactory in this case. The Ninject Kernel will automatically return null in this case.

    However when using the ServiceLocator interface, there is not a TryGet method like the Ninject Kernel offers when requesting instance, the TryGet method will actually scan the container for any registrations for a given interface and if it doesn’t find one it will return null for you.

    So I have to admit there wasn’t a way to really elegantly handle this. So to mimic this behaivor, which is returning a null when we weren’t able to find any registrations with the ServiceLocator, I had to wrap they request and catch the Microsoft.Practices.ServiceLocation.ActivationException, and if this exception was caught then we would return null so that the MVC runtime could take over and return default instances e.g. when requesting the IControllerFactory, ServiceLocator would return null, and MVC would take over and return the DefaultControllerFactory.

    Here’s the exception before wrapping the request:

    
        public class MyDependencyResolver : IDependencyResolver
        {
            public object GetService(Type serviceType)
            {
                    return ServiceLocator.Current.GetInstance(serviceType);
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                    return ServiceLocator.Current.GetAllInstances(serviceType);
            }
        }
    
    

  6. After wrapping the request to our container and returning null after scanning our container and not finding a registered instance for that interface, in our case the IControllerFactory.
    
        public class MyDependencyResolver : IDependencyResolver
        {
            public object GetService(Type serviceType)
            {
                try
                {
                    return ServiceLocator.Current.GetInstance(serviceType);
                }
                catch (Microsoft.Practices.ServiceLocation.ActivationException ex)
                {
                    return null;
                }
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                try
                {
                    return ServiceLocator.Current.GetAllInstances(serviceType);
                }
                catch (Microsoft.Practices.ServiceLocation.ActivationException ex)
                {
                    return null;
                }
            }
        }
    
    

    Obviously we could create a two extension methods for ServiceLocator maybe TryGet and TryGetAll, but in the interest of time we’ll skip that for now.

    Note, you can always use ServiceLocator everywhere else in your application when requesting for instance(s) for a given interface from your Kernel and leave our former implementation of MyDependencyResolver accessing the Ninject Kenerl directly if you are uncomfortable with wrapping it with the try/catch block to return null, so that the MVC runtime can fallback and return default instances e.g. IControllerFactory -> DefaultControllerFactory.

    Again, not the most elegant way, however when doing a deep dive on the TryGet method from the Ninject Kernel with Reflector I notice that we weren’t to far off from what Ninject was actually doing here.

  7. If you look at the GetValue method that we reflected on (which is what eventually get’s called by the Ninject’s _kernel.TryGet method, notice the return statement ending with the lambda expression SingleOrDefault()
    
    protected virtual object GetValue(Type service, IContext parent)
    {
        Ensure.ArgumentNotNull(service, "service");
        Ensure.ArgumentNotNull(parent, "parent");
        IRequest request = parent.Request.CreateChild(service, parent, this);
        request.IsUnique = true;
        return parent.Kernel.Resolve(request).SingleOrDefault<object>();
    }
    
    

    which returns null if a registration was not found for that interface in our Ninject container.

    Obviously wrapping our request and catching the ActivationException, we are swallowing the exception, which could create some difficulties in debugging true exceptions where things weren’t wired up correctly for Ninject to bind and activate instances to return to us.

  8. Now let’s see do a quick demonstration on using the ServiceLocator pattern other than in our MyDependencyResolver class. Let’s revisit the HomeController where we were orginall injected the IHelloWorldService in the constructor. An alternative way here is we can manually get this injected by requesting it with ServiceLocator.
    Before:
    
        public class HomeController : Controller
        {
            private readonly IHelloWorldService _helloWorldService;
    
            public HomeController(IHelloWorldService helloWorldService)
            {
                _helloWorldService = helloWorldService;
            }
    
            public ActionResult Index()
            {
                ViewBag.Message = _helloWorldService.Hello(@"Welcome to ASP.NET MVC!");
                return View();
            }
    
            public ActionResult About()
            {
                return View();
            }
        }
    
    

    After:

    
        public class HomeController : Controller
        {
            private readonly IHelloWorldService _helloWorldService;
    
            public HomeController()
            {
                _helloWorldService = ServiceLocator.Current.GetInstance<IHelloWorldService>();
            }
    
            public ActionResult Index()
            {
                ViewBag.Message = _helloWorldService.Hello(@"Welcome to ASP.NET MVC!");
                return View();
            }
    
            public ActionResult About()
            {
                return View();
            }
        }
    
    

    Now it’s really preference whether you want the MVC runtime to use our MyDependencyResolver in conjunction with Ninject to automagically figure out what needs to be injected like in our [before] case which was injecting the IHellowWorldService in the constructor of HomeController, or manually requesting it using ServiceLocator.

    Again I don’t see a wrong or right in eithier way, it’s just really preference, I prefer going with our before case where we let MyDependencyResolver and Ninject figure out what, when and where things automagically need to be injected, however I just wanted to demonstrate abstracting Ninject’s Kernel with ServiceLocator if you wanted to have the flexibility of easiliy swapping out our choice of IoC frameworks with mininmal refactoring or code change. 🙂

Download sample application: https://skydrive.live.com/redir.aspx?cid=949a1c97c2a17906&resid=949A1C97C2A17906!371&parid=949A1C97C2A17906!361