Search code examples
c#asp.netswaggersignalr

How do I correctly inject a SignalR hub in an ASP.NET controller?


I'm trying to use SignalR to send an update through a websocket when a request is made to an endpoint. But, I'm struggling to inject the relevant Controller with a hub dependency. I've got a controller with the following GET function:

[HttpGet]
public IEnumerable<Zaal> GetZaal()
{
    new BoekingUpdateHub().SendData("Test");
    foreach(Stoel s in _context.Stoel)
    {
        Console.WriteLine(s.Id);
    }
    return _context.Zaal;
}

Problem is, when I try to use this endpoint, the hub is not instantiated properly, as

public class BoekingUpdateHub : Hub
{
    public async Task SendData(string data)
    {
        await Clients.All.SendAsync("ReceiveData", data);
    }
}

Gives me a null reference exception on the await Clients.All, as Clients was null

I've tried using dependency injection to resolve this problem, but it gives me:

System.InvalidOperationException: Unable to resolve service for type 'WDPR.Hubs.BoekingUpdateHub' while attempting to activate 'WDPR.Controllers.ZaalController'.

I've looked around and I can't find a solution, I'm completely lost on how to implement this properly.


Solution

  • I figured it out. Rather than putting the Hub as an argument in the constructor, you add IHubContext as an argument, in this case the T is the Hub you want to actually use (in my case the hub is called MyHub)

    private readonly DbTheaterLaakContext _context;
    private readonly IHubContext<MyHub> _hubContext;
    
    public ZaalController(DbTheaterLaakContext context, IHubContext<BoekingUpdateHub> hubContext)
    {
        _context = context;
        _hubContext = hubContext;
    }
    

    Then, you can create the Hub inside one of the Endpoint functions:

    [HttpGet]
    public IEnumerable<Zaal> GetZaal()
    {
        new MyHub(_hubContext).SendData("Zaal GET request received");
    }
    

    Finally, make sure the IHubContext is an argument in the Hub's constructor:

    public class MyHub: Hub
    {
        protected IHubContext<MyHub> _context;
    
        public MyHub(IHubContext<MyHub> context)
        {
            this._context = context;
        }
        public async Task SendData(string data)
        {
            await _context.Clients.All.SendAsync("ReceiveData", data);
        }
    }
    

    Now, notice we're using the IHubContext as a source for our Clients, this prevents the Clients from being null. Doing these steps allows you to send information through a SignalR websocket when an endpoint receives a request.