Reputation: 406
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
?
Upvotes: 1
Views: 523
Reputation: 406
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());
}
}
Upvotes: 0
Reputation: 13233
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 Hub
s, you could also just leave out the When
condition.
Upvotes: 1