Search code examples
asp.net-coresignalr-hub

How to send constantly updates using .Net Core SignalR?


I am new to SignalR and I would like to build such app -- every second a hub sends current time to all connected clients.

I found tutorial, but it is for .Net Framework (not Core): https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-high-frequency-realtime-with-signalr So on one hand I don't know how to translate it to .Net Core SignalR, on the other hand I don't know how to write it from scratch (the limiting condition is the fact a hub is a volatile entity, so I cannot have state in it).

I need something static (I guess) with state -- let's say Broadcaster, when I create some cyclic action which in turn will send updates to clients. If such approach is OK, how to initialize this Broadcaster?

Currently I added such static class:

public static class CrazyBroadcaster
{
    public static void Initialize(IServiceProvider serviceProvider)
    {
        var scope = serviceProvider.CreateScope();
        var hub = scope.ServiceProvider.GetRequiredService<IHubContext<ChatHub>>();
        var sub = Observable.Interval(TimeSpan.FromSeconds(1)).Subscribe(_ => hub.Clients.All.SendAsync("Bar", DateTimeOffset.UtcNow));
    }
}

Yes, I know it is leaky. I call this method at the end of Startup.Configure, probably tons of violations here, but so far it is my best shot.


Solution

  • The missing piece was hosted service, i.e. the code that runs in the background -- https://learn.microsoft.com/en-US/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2.

    So my crazy class is now transformed into:

    public sealed class HostedBroadcaster : IHostedService, IDisposable
    {
        private readonly IHubContext<ChatHub> hubContext;
        private IDisposable subscription;
    
        public HostedBroadcaster(IHubContext<ChatHub> hubContext)
        {
            this.hubContext = hubContext;
        }
    
        public void Dispose()
        {
            this.subscription?.Dispose();
        }
    
        public Task StartAsync(CancellationToken cancellationToken)
        {
            this.subscription = Observable.Interval(TimeSpan.FromSeconds(1)).Subscribe(_ => hubContext.Clients.All.SendAsync("Bar", DateTimeOffset.UtcNow));
            return Task.CompletedTask;
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            this.subscription?.Dispose();
            return Task.CompletedTask;
        }
    }