views:

320

answers:

2

I'm trying to set up Ninject for the first time. I've got an IRepository interface, and a Repository implementation. I'm using ASP.NET MVC, and I'm trying to inject the implementation like so:

public class HomeController : Controller
{
    [Inject] public IRepository<BlogPost> _repo { get; set; }

    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        var b = new BlogPost
                    {
                        Title = "My First Blog Post!",
                        PostedDate = DateTime.Now,
                        Content = "Some text"
                    };

        _repo.Insert(b);

        return View();
    }

    // ... etc
}

And here's Global.asax:

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 = "" }  // Parameter defaults
        );

    }

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

    protected override IKernel CreateKernel()
    {
        IKernel kernel = new StandardKernel(new BaseModule());
        return (kernel);
    }
}

And here's the BaseModule class:

   public class BaseModule : StandardModule
    {
        public override void Load()
        {
            Bind<IRepository<BlogPost>>().To<Repository<BlogPost>>();
        }
    }

When I browse to the Index() action, though, I get "Object reference not set to an instance of an object" when trying to use _repo.Insert(b). What am I leaving out?

+2  A: 

Ninject 1.0 did not have MVC support out of the box. There are various ways of using MVC with Ninject 1.0 scattered around the web.

I'd recommend getting the latest code from the Ninject trunk, which includes MVC support. Then use the following as a starting point for your application:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using DemoApp.Models;
using Ninject.Core;
using Ninject.Framework.Mvc;

namespace DemoApp
{
    public class MvcApplication : NinjectHttpApplication
    {
        protected override 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 = "" }  // Parameter defaults
            );
        }

        protected override IKernel CreateKernel()
        {
            return new StandardKernel(new BaseModule(), new AutoControllerModule(Assembly.GetExecutingAssembly()));
        }
    }
}

There are a few things to highlight versus your original implementation...

  • Ninject has two implementations names NinjectHttpApplication - one in Ninject.Framework.Web and one in Ninject.Framework.Mvc. You appear to be using the former as the later contains a protected RegisterRoutes() method.
  • You need a way to give Ninject a hook into the controller creation, which is done using the ControllerBuilder. The Ninject.Framework.Mvc.NinjectHttpApplication registers the NinjectControllerFactory. You'll have to supply it yourself if using Ninject 1.0.
  • You need to register your controllers with the container. You could do it manually, but using the latest code provides and AutoControllerModule that automagically registers controllers for you!
John Clayton
Why do I need to use a ControllerBuilder and register controllers? I'm not trying to inject them (yet).
mgroves
Your null reference exception is from _repo being null because it isn't being injected into the controller.In order for Ninject to inject your repository into the controller Ninject has to create the controller. For Ninject to be able to create the controller it needs to have the controller registered, just like it needed to know about your repository. Finally you need to plug into MVC so that it uses Ninject to create the controller, which is what the ControllerBuilder handles.
John Clayton
+2  A: 

You need to add the AutoControllerModule to the list of modules you specify when creating the kernel, show below:

protected override IKernel CreateKernel()
{
    IKernel kernel = new StandardKernel(
                         new BaseModule(), 
                         new AutoControllerModule(Assembly.GetExecutingAssembly())
                     );
    return (kernel);
}

The AutoControllerModule is part of the MVC support in Ninject 1.x. It scans the assembly you provide to its constructor for MVC controller classes and auto-binds them. In the code, you have properly bound your repository, but Ninject is not in charge of activating your controllers. In order for your repository to be injected into an instance of your HomeController class, Ninject needs to be in charge of creating and activating controllers. Without the AutoControllerModule, MVC remains in charge of creating controllers; therefore, Ninject never gets a chance to inject any members. Once Ninject is in charge of creating and activating the controllers, the injection will occur as expected.

Think of the AutoControllerModule as finding all controllers and generating code like this (HomeController used as an example):

Bind<HomeController>.ToSelf();
Peter Meyer
Thank you for your help, but John answered first. I wish I could give you both credit :)
mgroves
You're welcome!
Peter Meyer