1
votes

I have set up Ninject to work with SignalR (hosted on IIS) as described in the answer to this question: SignalR 2 Dependency Injection with Ninject.

This works in most cases, except when the client is disconnecting from the hub the HttpContext.Current variable is null and thus Ninject can't inject the value and throws an exception.

I've read up on the issue and found out that most people recommend that the current HttpContext should be retrieved from IRequest.GetHttpContext() (which is accessible from the hubs context). Sadly this doesn't help when trying to inject the value (I could pass on the context from the hub, but that would defeat the purpose of having dependency injection).

Code example (some parts removed for brevity):

public class TestHub : Hub
{
    public TestHub(ITestService testService)
    {
        TestService = testService;
    }

    // When the disconnection request is issued, a ArgumentNullException
    // for the HttpContext construction is thrown
    public override Task OnDisconnected(bool stopCalled)
    {
        TestService.DoSomething();
    }
}

public class TestService : ITestService
{
    public TestService(HttpContextBase httpContext)
    {
        HttpContext = httpContext;
    }

    public void DoSomething()
    {
        // Service uses some data from the httpContext
        TestLogger.Log(HttpContext.User.Identity.Name);
    }
}

Is there any way to inject HttpContextBase into services that are in turn injected into SignalR hubs without accessing HttpContext.Current?

2
how do you access the hubs context?BatteryBackupUnit
I access the hubs context trough the the property Context (of the type HubCallerContext), residing in the base class Hub. That property then gives access to the http context trough Context.Request.GetHttpContext().betelgewse

2 Answers

1
votes

In case the HttpContext is actually available at construction time, you could use the following binding:

kernel.Bind<HttpContextBase>()
      .ToMethod(ctx => Context.Request.GetHttpContext())
      .WhenAnyAncestorMatches(ctx => typeof(Hub).IsAssignableFrom(ctx.Plan.Type));

The When condition checks whether the HttpContextBase is injected into a Hub (or derived class) or into any dependency of a Hub.

In case the HttpContextBase is only ever injected when contstructing Hubs, you could also just leave out the When condition.

0
votes

I have worked around the issue now, and thus this is not a solution to the problem, but an unclean way to mitigate it.

Since the missing HttpContext only happens on client disconnects, I have first of marked all my injected services as Lazy<T>, so they don't get resolved immediately, but only when they are accessed. After applying this change, the exceptions are thrown only when code in the SignalR OnDisconnected event of the hub is triggered. So I had to modify the code in that is executed in the OnDisconnected method to use (or pass in as parameter) the context retrieved directly from the hub. In my case not much code gets executed in there, but it could become a problem if more is required in the future.

The patch applied to the sample code from my question:

public class TestHub : Hub
{
    public TestHub(Lazy<ITestService> testService)
    {
        TestService = testService;
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        DoSomethingThatInvolvesHttpContext(Context.Request.GetHttpContext());
    }
}