Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine an EventHandler with a SignalR hub with .NET core?

Using .NET Core 2.1, where I have a service like this:

public class OrderService : IOrderService
{
    public event EventHandler<OrderUpdatedEvent> OrderUpdatedEventHandler; 

    ...
}

I also created a SignalR hub like this:

public class OrderHub : Hub
{
    private OrderService _orderService;
    private EventHandler<OrderUpdatedEvent> _eventHandler;

    public OrderHub(OrderService orderService)
    {
        Console.WriteLine("OrderHub created...");
        _orderService = orderService;
        _eventHandler = (sender, updateEvent) => { SendUpdateOverWebsocket(updateEvent); };
        _orderService.OrderEventHandler += _eventHandler;
        Console.WriteLine("Event handler added!");
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        _orderService.OrderUpdatedEventHandler -= _eventHandler;
    }

In the OrderService I have this line:

OrderUpdatedEventHandler?.Invoke(this, new OrderUpdatedEvent(orderId, ...));

The problem is that OrderUpdatedEventHandler is always null because the constructor of OrderHub does not seem to be created at application startup.

The OrderService is registered as singleton:

services.AddSingleton<IOrderService, OrderService>();

I did find in the docs that .NET will only create singletons when it is "used". Which I understood as "a controller that has a dependency on this singleton is hit for the first time via a HTTP REST call".

What is also not good in my code is that OrderHub declares the constructor DI parameter as OrderService instead of IOrderService. I could probably get around it by directly injecting the hub into my OrderService, but I want to use events to have a loose coupling between the service and putting stuff on a websocket when something changes.

like image 570
Wim Deblauwe Avatar asked Oct 20 '25 13:10

Wim Deblauwe


1 Answers

As you already feared, the OrderHub is not created at application startup. The Hub will be created, when a client connects or invokes a method on the Hub. Another problem is, that the Hub gets directly disposed after the client has connected or a hub method has been invoked. So in your case, the Hub will immediately unsubscribe from the EventHandler. In addition the Hub instance is no singleton.

I would suggest the following solution, when your primary goal is loose coupling:

Create a IOrderBroadcaster interface, to be able to change the implementation from SignalR to another technology later:

public interface IOrderBroadcaster
{
    Task SendUpdate(string order);
}

The implementation for the SignalR version would look like that:

public class WebSocketOrderBroadcaster : IOrderBroadcaster
{
    private IHubContext<OrderHub> orderHubContext;

    public WebSocketOrderBroadcaster(IHubContext<OrderHub> orderHubContext)
    {
        this.orderHubContext = orderHubContext;
    }

    public Task SendUpdate(string order)
    {
        return this.orderHubContext.Clients.All.SendAsync("Update", order);
    }
}

Also take care, that the new broadcaster is registered for DI in Startup.cs:

services.AddSingleton<IOrderBroadcaster, WebSocketOrderBroadcaster>();

For this solution your Hub can remain empty:

public class OrderHub : Hub
{
}

In your OrderService you then can inject the IOrderBroadcaster and call your update method:

public class OrderService : IOrderService
{
    private IOrderBroadcaster broadcaster;

    public OrderService(IOrderBroadcaster broadcaster)
    {
        this.broadcaster = broadcaster;
    }

    public void UpdateOrder(string order)
    {
        this.broadcaster.SendUpdate(order);
    }
}

With this solution you have achieved a loose coupling and also don't need EventHandlers, which should be avoided anyway in my opinion, when it's possible.

Happy coding!

like image 113
Daniel Beckmann Avatar answered Oct 22 '25 02:10

Daniel Beckmann



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!