1

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?

Community
  • 1
  • 1
betelgewse
  • 406
  • 4
  • 12
  • how do you access the hubs context? – BatteryBackupUnit Nov 24 '15 at 07:31
  • 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 Nov 24 '15 at 15:15

2 Answers2

1

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.

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • 1
    A slight misunderstanding: `Context` is a property of the "to be instanced class" TestHub, not a static property. In other words, this isn't so much as a Ninject question, but rather a SignalR one. Thanks for the effort, though. – betelgewse Nov 25 '15 at 18:40
  • oh ok. Yeah i'd say it becomes more of a SignalR problem then, to access the context before the `Hub` is created. I think the context should be available before the `Hub` is created, but the question is how to access it. Should i leave the answer - as it shows at least how to do a binding for something injected into a `Hub` or dependency, which might be usable for solution once one finds out how to access the context - or will it be irrelevant? In this case i will delete the answer. – BatteryBackupUnit Nov 26 '15 at 11:18
  • In my opinion leave it, it shows the 2nd half of the solution - once the hub context can be retrieved. – betelgewse Nov 26 '15 at 21:01
0

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());
    }
}
betelgewse
  • 406
  • 4
  • 12