Andrey Kotov
Andrey Kotov

Reputation: 1414

How to force a server to send an event-data constantly, using ASP.NET MVC + SignalR?

ExportClient class has OnTickRecieved event, which helps me to receive some data (bid prices from market). All I want - is to receive this data real-time on my charts in browser. When I press Go button on UI-side, it calls Go() method in controller, and then nothing is happening. And it's logical - because after request on server, controller is destroyed.

My question is: how to force a server to send me an event-data constantly?

Controller code:

public class ChartsController : Controller
{
    [HttpGet]
    public void Go()
    {
        var exportClient = new ExportClient();
        exportClient.TickRecieved += exportClient_TickRecieved;
    }

    private void exportClient_TickRecieved(object sender, TickRecievedEventArgs args)
    {
        ImpulserHub.SendBidPrice(args.Bid);
    }
}

Hub code:

[HubName("impulserHub")]
public class ImpulserHub : Hub
{
   public static void SendBidPrice(double bid)
   {
       var hubContext = GlobalHost.ConnectionManager.GetHubContext<ImpulserHub>();
       hubContext.Clients.All.sendBidPrice(bid);
   }
}

And I have tested SignalR, this code works fine:

[HttpGet]
public void Go()
{
   ImpulserHub.SendBidPrice(3.3333333); // I have received this number on UI
}

Upvotes: 3

Views: 482

Answers (1)

Carl St-Laurent
Carl St-Laurent

Reputation: 58

The simplest way would be to have your export client as a singleton or as a static variable and register to you event in the global scope (probably in your Application_Start() method from Global.asax.cs). Your hub code should also be moved out since hubs are transient like controllers.

This is how it would look like:

private ExportClient _exportClient;
private IHubContext _impulserHub;

protected void Application_Start()
{
    _exportClient = new ExportClient();
    exportClient.TickRecieved += exportClient_TickRecieved;
    _impulserHub = GlobalHost.ConnectionManager.GetHubContext<ImpulserHub>();
}

private void exportClient_TickRecieved(object sender, TickRecievedEventArgs args)
{
    _impulserHub.Clients.All.sendBidPrice(args.Bid);
}

There is still an issue remaining with this code. IIS will tear down websites that are not actively receiving requests. This means that the code may stop working at any time even if the event is triggered. Managing application teardown is hard since the state must be saved/transferred between application start and stop. Unless you can set IIS never to tear down your application (most of the time impossible on shared or cloud hosting), you should try to use the HangFire library which is available as a nuget package.It was specifically designed for that use case and with a bit of refactoring, your code could look like that:

private ExportClient _exportClient;
private IHubContext _impulserHub;
protected void Application_Start()
{
    _exportClient = new ExportClient();
    exportClient.TickRecieved += exportClient_TickRecieved;

    _impulserHub = GlobalHost.ConnectionManager.GetHubContext<ImpulserHub>();

    BackgroundJob.Schedule(() => _impulserHub.Clients.All.sendBidPrice(_exportClient.GetBid()), TimeSpan.FromSeconds(5));
}

Upvotes: 2

Related Questions