g_b
g_b

Reputation: 12438

How does ASP.NET MVC know what controller constructor to call?

I'm currently starting ASP.NET MVC 4 and in the book I am reading, the author introduced Ninject for dependency injection. He created a custom dependency resolver (which I don't fully understand how it works, but I think it's use is to easily manage dependency resolution).

Here is the controller code:

public class HomeController : Controller
{
    private Product[] products = {
        new Product {Name = "Kayak", Category = "Watersports", Price = 275M},
        new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M},
        new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M},
        new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M}
    };

    private IValueCalculator calc;
    public HomeController(IValueCalculator calcParam)
    {
        calc = calcParam;
    }

    public ActionResult Index()
    {
        ShoppingCart cart = new ShoppingCart(calc) { Products = products };
        decimal totalValue = cart.CalculateProductTotal();
        return View(totalValue);
    }
}

And the custom dependency resolver:

public class NinjectDependencyResolver : IDependencyResolver
{
    private IKernel kernel;
    public NinjectDependencyResolver()
    {
        kernel = new StandardKernel();
        AddBindings();
    }

    public object GetService(Type serviceType)
    {
        return kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return kernel.GetAll(serviceType);
    }

    private void AddBindings()
    {
        kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
    }
}

In Application_Start(), the resolver was set:

DependencyResolver.SetResolver(new NinjectDependencyResolver());

Questions:

  1. How did ASP.NET MVC knew what controller constructor to call? I assumed it was going to call the default constructor but there isn't one, I tried adding one, still the constructor with the parameter was called.

  2. How did ASP.NET MVC passed the IValueCalculator to the constructor?

Upvotes: 1

Views: 4103

Answers (2)

LukLed
LukLed

Reputation: 31862

This is quote from documentation (https://github.com/ninject/ninject/wiki/Dependency-Injection-With-Ninject):

When asked to instantiate an object, Ninject will look at the type’s available public constructors and pick the one with the most parameters it knows how to resolve — or the parameterless one if there aren’t any suitable ones (there’s an attribute that can be used to override this – see Constructor injection for the nitty gritty).

and another quote from here (https://github.com/ninject/ninject/wiki/Injection-Patterns):

If a constructor has an [Inject] attribute, it is used (but if you
apply the attribute to more than one, Ninject will throw a
NotSupportedException at runtime upon detection).

If no constructors have an [Inject] attribute, Ninject will select the one with the most parameters that Ninject understands how
to resolve.

If no constructors are defined, Ninject will select the default parameterless constructor (assuming there is one).

And how did ASP.NET MVC passed the IValueCalculator to the constructor?

Whole MVC pipeline uses controller factories to build instance of specific controller. Controller factory uses DependencyResolver inside. That is why dependency resolver is registered at application start:

DependencyResolver.SetResolver(new NinjectDependencyResolver());

As I wrote earlier, Ninject knows how to find correct constructor and uses it to build instance. It finds this constructor:

public HomeController(IValueCalculator calcParam)

Here dependency resolver is used to find implementation of IValueCalculator, which was defined here:

private void AddBindings()
{
    kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
}

Ninject here tries one again to find constructor for class LinqValueCalculator. If constructor had dependencies, they would be injected using the same mechanism.

Upvotes: 6

CodeCaster
CodeCaster

Reputation: 151672

How did ASP.NET MVC know...

It knows by asking the DependencyResolver. MVC will call GetService() when a controller must be created.

The default MVC resolver only handles parametersless constructors, but in your case you have assigned your custom resolver, where the GetService() call will be relayed to NInject's IKernel.TryGet(Type).

NInject will then search for the type HomeController and its constructors, and will find the one with the IValueCalculator parameter.

In the AddBindings() method you have instructed NInject to inject LinqValueCalculator every time a IValueCalculator is required, so it does that, instantiates the type using reflection and returns the initialized controller to MVC.

Upvotes: 4

Related Questions