Ninject + MVC3 is not injecting into controller
Asked Answered
N

3

14

I have used the NuGet Ninject MVC3 extension and have been unable to get it to inject into a controller upon request. It doesn't seem to have bound, as MVC is looking for the paramaterless constructor. Here's the stack trace:

    [MissingMethodException: No parameterless constructor defined for this object.]
       System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
       System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98
       System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241
       System.Activator.CreateInstance(Type type, Boolean nonPublic) +69
       System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +67

    [InvalidOperationException: An error occurred when trying to create a controller of type 'MyApp.Presentation.Controllers.SearchController'. Make sure that the controller has a parameterless public constructor.]
       System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +182
       System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) +80
       System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName) +74
       System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +199
       System.Web.Mvc.<>c__DisplayClass6.<BeginProcessRequest>b__2() +49
       System.Web.Mvc.<>c__DisplayClassb`1.<ProcessInApplicationTrust>b__a() +13
       System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
       System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
       System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Func`1 func) +124
       System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +98
       System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +50
       System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
       System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8862676
       System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184

And the controller i'm trying to inject into (it takes a base class at the moment):

public class SearchController : MainBaseController
{

    private readonly MyApp.Domain.Tutor.TutorSearch TutorSearch;
    protected static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    [Ninject.Inject]
    public SearchController(MyApp.Domain.Tutor.TutorSearch tutorSearch)
    {
        this.TutorSearch = tutorSearch;
    }

    .... (no other constructors)
}

And the relevant part of my NinjectWebCommon.cs:

    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        INinjectModule[] modules = new INinjectModule[]
        {
            new Domain.DomainModule()
        };

        kernel.Load(Assembly.GetExecutingAssembly());
        kernel.Load(modules);

        kernel.Bind<MyApp.Domain.Tutor.TutorSearch>().ToSelf();

        // ***************
        var t = kernel.Get<MyApp.Presentation.Controllers.SearchController>();
    }

Finally, here is DomainModule:

public class DomainModule : NinjectModule
{
    public override void Load()
    {
        Bind<Data.Profile.IProfileProvider>().To<Data.Profile.XmlProfileProvider>();
        Bind<Data.Subject.ISubjectProvider>().To<Data.Subject.XmlSubjectProvider>();
    }
}

As you can see in my NinjectWebCommon.cs at the line marked with asterisks, I have tried constructing the controller explicitly as a test. This works perfectly fine, with the properly injected Xml..Providers.

Is there something I have missed? I don't know enough about what goes on under the hood of the NuGet MVC3 extension and so have no idea at which point the controller bindings are failing.

Any pointers on what to look at would be very much appreciated, thanks.

Newsboy answered 15/1, 2013 at 17:32 Comment(1)
Traw, is there a particular reason that you're using concrete implementations rather than Interfaces.?? I've added a quick sketch of how this should lookEighth
N
18

So after quite a while of desperate tinkering, I finally found my problem. I was referencing the MVC4 (System.Web.Mvc) DLLs and had not redirected the binding of the older 3.0.0.0 version to 4.0.0.0:

  <dependentAssembly>
    <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
    <bindingRedirect oldVersion="1.0.0.0" newVersion="4.0.0.0" />
    <bindingRedirect oldVersion="2.0.0.0" newVersion="4.0.0.0" />
    <!-- The following line was missing -->
    <bindingRedirect oldVersion="3.0.0.0" newVersion="4.0.0.0" /> 
  </dependentAssembly>

At least, I hope that's a decent solution.

Edit: as suggested below, the following line is more succinct:

  <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
Newsboy answered 16/1, 2013 at 8:49 Comment(7)
this helped solved a similar problem I was having here...Plasticize
Wouldn't bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" cover those three cases in one line?Begga
Where can I find this file? I'm having the same problem.Petree
Nevermind. I found it in a web-config of a different project. For some reason, the dependentAssembly section of my config was missing.Petree
I really hope this is fixed by year 2053, or even sooner.Binnacle
This one ruined my afternoon, there needs to be a much easier way to debug issues like this.Fad
Why would you want to downgrade the version of MVC of the sake of a Ninject library?Lavabo
E
6

Using Visual Studio and Nuget you need to run this command

Install-Package Ninject.MVC3

(replace the 3 depending on which MVC version you are running)

This will solve your problem

Eyelet answered 7/9, 2014 at 18:22 Comment(1)
This more or less worked for me. I thought I had Ninject.MVC5 (Ninject.Web.Mvc) installed but it wasn't. I just had Ninject and Ninject.Web.Common for some reason.Ticktacktoe
E
1

Traw, is there a particular reason that you're using concrete implementations rather than Interfaces.?? As it stands, you are gaining zero benefit from DI'ing the class straight in (and i'm surprised that you're getting anything to work) i would refactor and put everything behind an interface, then bind the interface to the concrete implementation.

Here's a quick shot at how that might look:

// 'imagined' ISearchType interface
public interface ISearchType
{
    int Id { get; set; }
    string LastName { get; set; }
}

// 'imagined' TutorSearch concrete class
public class TutorSearch : ISearchType
{
    public int Id { get; set; }
    public string LastName { get; set; }
}

// your revised controller code
private readonly ISearchType _tutorSearch;
public SearchController(ISearchType searchType)
{
    _tutorSearch = searchType;
}

then, in your registering of the services (ninject), you'd add:

kernel.Bind<ISearchType>().To<TutorSearch>();

That should get you a little further. Also, var t = kernel.Get<...> actually does nothing at all - just in case you were wondering about what had happened to 'it'.

Have fun...

Eighth answered 15/1, 2013 at 18:15 Comment(4)
Hi Jim. Firstly, thanks for your reply. The reason I am using the concrete implementation is because at this stage I haven't gotten around to factoring it out, and the class in question has the dependencies I wish to inject. I was under the impression that Ninject would be fine with injecting a concrete implementation or is that not the case? The line with 'var t = kernel.Get<...>' was simply a test for me to look at in my debugger, and it worked fine - Ninject constructed the controller perfectly. It just didn't upon a web request. I also tried passing an interface and got the same result!Newsboy
Hi Traw - glad you got everything working as hoped for. however, i would discourage injecting concrete classes via ninject (even if you can). what advantage do you get?? you'd be as well just to create the class in the empty constructor as it's never going to change implementation. my advice would be to START with interfaces and code your concrete implementations against those. that would be best practice as you then only have to refactor code in the concrete classes. plus, for unit testing, there's no way you can do any of that based on the code example that you've provided.Eighth
also!! another advantage of coding against an interface is the fact that you can farm out work to other developers and know that the implementations will be injectable into exisiting functionality. if you take anything away from this thread, please promise me that you'll start again and work up from interfaces -please, please pretty please :) good luck..Eighth
Thanks for your enthusiastic plea Jim, and rest assured that I do indeed begin with interfaces where appropriate. In this case I haven't because there was no foreseeable requirement and I needed something running as quickly as possible. I am injecting it here purely because I am having Ninject inject its dependencies which are provided by a module in a lower layer. This allows the presentation layer to be free of any knowledge of the lower layer's dependencies. Also, it's not quite as bad as it looks. I entirely agree with all of the benefits you have stated though :)Newsboy

© 2022 - 2024 — McMap. All rights reserved.