Scoped services in ASP.NET Core with SignalR Hubs
08:29 27 Jun 2019

I have introduced SignalR into my ASP.NET Core 2 project, but I'm having some issues using a scoped service that I normally use in my controllers. I feel like the problem may be due to the difference in lifecycles between HTTP requests, websockets and hubs.

On each HTTP request, middleware reads the Authorization token and updates some properties (e.g. id, claims, etc.) on a scoped service (IUser) for the request. I use this service in all of my controllers with no issue. To get this to work with SignalR, I am sending an access_token query parameter and using some other middleware beforehand to add this query parameter as a header which works fine.

The problem arises when I am trying to access the IUser service in my SignalR hub. On construction of the hub, the IUser that gets injected has none of the properties set, despite the middleware for the /hub request just setting them.

If I set the service to be a singleton then it works, but IUser should never persist longer than a single request.

How should I go about setting an IUser for a specific SignalR connection?

// Startup.cs - User has several settable properties 
services.AddScoped();
// User Middleware
public class UserMiddleware
{
    private readonly RequestDelegate _next;

    public UserMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public Task Invoke(HttpContext context)
    {
        // retrieve service for this request
        var user = context.RequestServices.GetService();

        // set some properties on the User from auth token
        // e.g. User.Id = 1;

        return _next(context);
    }
}
[Authorize(Roles = Claim.Names.Read)]
public class MyHub : Hub
{
    private readonly IUser _user;

    public MyHub(IUser user)
    {
        // user that gets injected has null properties
        _user = user;
    }

    public async Task Foo()
    {
        // do work with the _user credentials
    }
}
public class DownloadController : Controller
{
    private readonly IUser _user;

    public DownloadController(IUser user)
    {
        // user gets injected and has properties set
        _user = user;
    }
}
c# asp.net asp.net-core .net-core signalr