How can I force Ninject to exception when largest constructor has unresolvable dependencies?

194 views Asked by At

According to the documentation for Ninject:

The primary DI pattern is Constructor Injection. When activating an instance of a type Ninject will choose one of the type’s constructors to use by applying the following rules in order:-

  1. 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).

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

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

It seems there is a gotcha in #2 that is hard to miss if you glance at this list quickly. The gotcha is in the that Ninject understands how to resolve.

Consider the following scenario:

// Defined in MyController.cs
public void MyController(IMyDependency depencency) { }

// Defined in MyController.generated.cs for T4
public void MyController() { }

If Ninject has not had any types bound to IMyDependency and then you call kernel.Resolve<MyController>(), it will not exception but instead pass, using the parameterless constructor instead.

However, this is most definitely NOT desired behavior as I want to be notified of resolution failings immediately, and the default Ninject functionality also makes it impossible to test IOC resolutions in coded tests.

Is there any way to have Ninject exception if resolution fails on the constructor with the most parameters and not fall back to other constructors if possible?

1

There are 1 answers

3
spender On BEST ANSWER

Implement your own IConstructorScorer, probably by subclassing the default StandardConstructorScorer and giving low scores to constructors that you don't like the look of...

Ninject runs each constructor through the IConstructorScorer interface to determine which constructor it should use to instantiate the class

Details of how to do this are in the documentation on github.

In your case, it might look something like this:

public class MyScorer : StandardConstructorScorer
{
    public override int Score(IContext context, ConstructorInjectionDirective directive)
    {
        //has more than one constructor
        //and the constructor being considered is parameterless
        if (directive.Constructor.GetType().GetConstructors().Count() > 1 
            && !directive.Targets.Any())
        {
            //give it a low score
            return Int32.MinValue;
        }
        return base.Score(context, directive);
    }
}