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);
}
}
Let check the test result first, and usually we implement this requirement like below steps.
Project Structure
There is no INotificationHub in my sample project.
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();
}
}
}