How do I inject into some generic asp.net http handler using Ninject?
Asked Answered
C

3

7

I'm a newbie using Ninject and I can't figure out how to inject into my generic http handler. I have a MVC3 project and I'm injecting my services into controllers with no problem at all. This is what I got in my Ninject App_start class for registering services:

        private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<NLSubscriber.Core.Service.Repository.INLUserRepository>().To<NLSubscriber.Core.Service.Repository.EFDAL.EFNLUserRepository>().InRequestScope();
        kernel.Bind<Neticon.Mvc.Helpers.IConfigHelper>().To<Neticon.Mvc.Helpers.AzureEnabledConfigHelper>().InSingletonScope();
        kernel.Bind<Neticon.Security.Service.IAuthenticationService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateAuthenticationService()).InRequestScope();
        kernel.Bind<Neticon.Security.Service.IMembershipService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateMembershipService()).InRequestScope();
        kernel.Bind<Neticon.Security.Service.IRoleManagerService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateRoleManagerService()).InRequestScope();

When I try to get some service from my generic handler by using property injection (with [inject] attribute) I always get null. This is how my handler looks like:

    public class SubscriberHandler : IHttpHandler
{
    [Inject]
    public INLUserRepository userRep { get; set;}

    public void ProcessRequest(HttpContext context)
    {
        var users = userRep.GetUsers(); //userRep is always null here
    }


    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

I have also tried doing it like this:

    readonly INLUserRepository userRep;

    public SubscriberHandler()
    {

        using (IKernel kernel = new StandardKernel(new App_Start.NJRepositoryModule()))
        {
            userRep = kernel.Get<INLUserRepository>();
        }
    }

but I'm getting an exception: "Error loading Ninject component ICache. No such component has been registered in the kernel's component container. Suggestions: 1) If you have created a custom subclass for KernelBase, ensure that you have properly implemented the AddComponents() method. 2) Ensure that you have not removed the component from the container via a call to RemoveAll(). 3) Ensure you have not accidentally created more than one kernel."

That's suggesting me that I'm not supposed to instantiate more than one kernel in my application, right? What am I doing wrong? Thanks

Crossruff answered 4/5, 2012 at 10:4 Comment(1)
Possible duplicate of #3629972Lineup
S
8

You could use the dependency resolver:

public class SubscriberHandler : IHttpHandler
{
    public INLUserRepository userRep { get; private set; }

    public SubscriberHandler()
    {
        userRep = DependencyResolver.Current.GetService<INLUserRepository>();
    }

    public void ProcessRequest(HttpContext context)
    {
        var users = userRep.GetUsers(); //userRep is always null here
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

I am expecting to get negative feedback from this answer because the service locator pattern is considered by many as an anti-pattern.

But I am not sure whether NInject allows you to use constructor injection for HTTP handlers because they are instantiated by the ASP.NET runtime.

Sherbrooke answered 4/5, 2012 at 10:7 Comment(4)
Thanks Darin. This works for me for now. Regarding the anti-pattern, as you can see, I've already passed that...I mean, using inject attribute or any other direct reference to Ninject isn't pretty either. One comment though, I made userRep private readonly, no need to make it public...Crossruff
Wont -1 people trying to help (even if they are showing people how to wield a SL!), but this answer is a dup of https://mcmap.net/q/1477536/-httphandler-property-injection-using-ninject-returning-null and the question is a dup of this tooTelegraphone
@RubenBartelink, in the dup link you have posted they use ServiceLocator.Current.GetInstance<IFile>(); whereas in my example I use DependencyResolver.Current.GetService<INLUserRepository>(); which is an ASP.NET MVC 3 specific class.Sherbrooke
@Darin Dimitrov: Fair point. (But they are both SLs so its still the same from my perspective)Telegraphone
Q
6

The composition root for IHttpHandlers is the IHttpHandlerFactory. You can create a custom IHttpHandlerFactory that uses Ninject to create an instance of your IHttpHandler. That way you can use constructor injection.

Quicksand answered 4/5, 2012 at 10:47 Comment(3)
Can you stick this onto #3629972 and VTC this please.Telegraphone
Remo, do you have an example demonstrating this anywhere?Lawerencelawes
Hi, is there any sample code for this. How do we got the handler instance.Dihydric
A
2

I see you have a "RegisterServices" method in your snippet which suggests you're already using Ninject.Web.Common. What you might not know about NinjectWebCommon.cs is it uses a Bootstrapper class which contains a singleton instance of the Ninject kernel.

As Remo mentioned above, IHttpHandlerFactory is the composition root for IHttpHandler instances and as such you will need to create an implementation of this interface and add the necessary configuration elements to your web.config.

MyHandlerFactory.cs:

public class MyHandlerFactory : IHttpHandlerFactory
{
    public bool IsReusable => false;

    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
    {
       // the bootstrapper class uses the singleton pattern to share the Ninject Kernel across your web app's ApplicationDomain
       var kernel = new Bootstrapper().Kernel;

       // assuming you have only one IHttpHandler binding in your NinjectWebCommon.cs
       return kernel.Get<IHttpHandler>();
    }

    public void ReleaseHandler(IHttpHandler handler)
    {
       // nothing to release
    }
}

Now, add the necessary config elements for your new handler factory...

Web.config:

  <system.web>
    <httpHandlers>
      <add verb="GET" path="*.customThingImade" type="MyNamespace.MyHandlerFactory, MyAssemblyWhereIPutMyHandlerFactory, Version=1.0.0.0, Culture=neutral" />
    </httpHandlers>
  </system.web>
  <system.webServer>
    <handlers>
      <add name="MyHandlerFactory" verb="GET" path="*.customThingImade" type="MyNamespace.MyHandlerFactory, MyAssemblyWhereIPutMyHandlerFactory, Version=1.0.0.0, Culture=neutral" preCondition="integratedMode" />
    </handlers>
  </system.webServer>

Finally, add a binding for your IHttpHandler implementation...

NinjectWebCommon.cs:

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<NLSubscriber.Core.Service.Repository.INLUserRepository>().To<NLSubscriber.Core.Service.Repository.EFDAL.EFNLUserRepository>().InRequestScope();
    kernel.Bind<Neticon.Mvc.Helpers.IConfigHelper>().To<Neticon.Mvc.Helpers.AzureEnabledConfigHelper>().InSingletonScope();
    kernel.Bind<Neticon.Security.Service.IAuthenticationService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateAuthenticationService()).InRequestScope();
    kernel.Bind<Neticon.Security.Service.IMembershipService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateMembershipService()).InRequestScope();
    kernel.Bind<Neticon.Security.Service.IRoleManagerService>().ToMethod(m => Neticon.Security.Service.SecurityServiceFactory.CreateRoleManagerService()).InRequestScope();

    // the binding for your handler factory
    Bind<IHttpHandler>().To<SubscriberHandler>();
}
Appledorf answered 31/10, 2018 at 17:30 Comment(1)
I read with later versions of ninject web common you can just inherit from Ninject.Web.HttpHandlerBase and it's supposed to work. Try that first if you're reading this.Appledorf

© 2022 - 2024 — McMap. All rights reserved.