Search code examples
c#dependency-injectionsignalrasp.net-core-signalr

How to properly setup a service that interacts with SignalR Hub


I am trying to create a service to interact with a SignalR Hub, which I've called NotificationHub. I've marked where Clients is null using this method. Is this the correct way to setup dependency injection with the Hub such that it can used with a service?

Notification Hub

public interface INotificationHub
    {
        Task Send(Notification notification);
    }
public class NotificationHub : Hub, INotificationHub
    {
        public async Task Send(Notification notification)
        {
            //Clients is null here
            await Clients.All.SendAsync(notification.Type, notification);
        }
    }

Notification Service

public interface INotificationService
    {
        Task SendNotificationToAll(Notification notification);
    }
public class NotificationService : INotificationService
    {
        private readonly INotificationHub _hubContext;

        public NotificationService(INotificationHub hubContext)
        {
            _hubContext = hubContext;
        }

        public async Task SendNotificationToAll(Notification notification)
        {
            await _hubContext.Send(...);
        }
    }

Trying to call from a controller:

public class SomeController : Controller {

        private readonly INotificationService _notificationService;
        public SomeController (..., INotificationService notificationService)
        {            
            _notificationService = notificationService;
        }
        [HttpPost, Route("notification")]
        public async Task<IActionResult> Notification()
        {
            
            _notificationService.SendNotificationToAll(notification1);
        }
        
}


Solution

  • Let check the test result first, and usually we implement this requirement like below steps.

    enter image description here

    Project Structure

    There is no INotificationHub in my sample project.

    enter image description here

    NotificationHub.cs

    using SignalrSample.Models;
    using Microsoft.AspNetCore.SignalR;
    
    namespace SignalrSample
    {
        public class NotificationHub : Hub
        {
            public async Task SendMessage(Notification notification)
            {
                await Clients.All.SendAsync("ReceiveNotification", notification.Type, notification);
            }
        }
    }
    

    INotificationService.cs

    using SignalrSample.Models;
    
    namespace SignalrSample.IService
    {
        public interface INotificationService
        {
            Task SendNotificationToAll(Notification notification);
        }
    }
    

    NotificationService.cs

    using Microsoft.AspNetCore.SignalR;
    using SignalrSample.IService;
    using SignalrSample.Models;
    
    namespace SignalrSample.Service
    {
        public class NotificationService : INotificationService
        {
            // We should use private readonly IHubContext<NotificationHub> _hubContext;, 
            // then we can find Clients
            private readonly IHubContext<NotificationHub> _hubContext;
    
            public NotificationService(IHubContext<NotificationHub> hubContext)
            {
                _hubContext = hubContext;
            }
    
            public async Task SendNotificationToAll(Notification notification)
            {
                await _hubContext.Clients.All.SendAsync("ReceiveNotification", notification.Type, notification);
            }
        }
    }
    

    Model - Notification.cs

    namespace SignalrSample.Models
    {
        public class Notification
        {
            public string? Type { get; set; }
            public string? Message { get; set; }
        }
    }
    

    Test method inside the HomeController

    [HttpPost, Route("notification")]
    public async Task<IActionResult> Notification([FromBody] Notification notification)
    {
        await _notificationService.SendNotificationToAll(notification);
        return Ok();
    }
    

    Program.cs

    using SignalrSample.IService;
    using SignalrSample.Service;
    
    namespace SignalrSample
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                var builder = WebApplication.CreateBuilder(args);
    
                // Add services to the container.
                builder.Services.AddControllersWithViews();
                builder.Services.AddSignalR();
                builder.Services.AddTransient<INotificationService, NotificationService>();
    
                var app = builder.Build();
    
                ...
    
                app.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
    
                app.MapHub<NotificationHub>("/notificationHub");
    
                app.Run();
            }
        }
    }