Search code examples
javascriptc#asp.netsignalrsignalr-hub

SignalR receives more and more messages after refreshing the page


I wrote a web application in ASP.NET that exchange data with a C# program via SignalR. Here the relevant parts of my code. Of course if I forgot something important to address my issue, please ask in the comments.

ASP.NET

HubClientManager.cs

using Microsoft.AspNet.SignalR.Client;
using System;
using System.Threading.Tasks;
using System.Diagnostics;

namespace MyProject
{
    public class HubClientManager
    {
        public String GetServerURI()
        {
            return String.Concat("http://192.168.1.103", ":8080", "/signalr");
        }

        public IHubProxy GetHub()
        {
            var hubConnection = new HubConnection(GetServerURI());
            var hub = hubConnection.CreateHubProxy("liveHub");

            try
            {
                var task = hubConnection.Start();
                Task.WhenAll(task).Wait();
            }
            catch (Exception e)
            {
                Debug.WriteLine(e);
            }
            return hub;
        }
    }
}

Index.cshtml

<script src="@ViewBag.serverURI/hubs"></script>
<script>
    $(function () {   
        $.connection.hub.url = "@ViewBag.serverURI";
        var hub = $.connection.liveHub;

        hub.client.orderStatus = function (opened, suspended, running, closed, cancelled) {
            $("#cntOpened").text(opened);
            $("#cntSuspended").text(suspended);
            $("#cntRunning").text(running);
            $("#cntClosed").text(closed);
            $("#cntCancelled").text(cancelled);
        };

        $.connection.hub.logging = true;

        $.connection.hub.start().done(function () { });

        $.connection.hub.error(function (error) {
            console.log('SignalR error: ' + error)
        });
    });
</script>

MachinesController.cs

public class MachinesController : Controller
{
    private HubClientManager _manager = new HubClientManager();
    private IHubProxy _hub;

    public MachinesController()
    {
        _hub = _manager.GetHub();
    }

    // GET: Machines
    public ActionResult Index()
    {
        ViewBag.serverURI = _manager.GetServerURI();
        return View(...);
    }

    ...
}

C#

Hub.cs

namespace MyProject.Hubs
{
    public class LiveHub : Hub
    {
        private readonly Erp _erp;

        public LiveHub(Erp erp)
        {
            _erp = erp;
            _erp.HubSendOrderStatus += Erp_HubSendOrderStatus;
        }

        private void Erp_HubSendOrderStatus(int arg1, int arg2, int arg3, int arg4, int arg5)
        {
            Clients.All.orderStatus(arg1, arg2, arg3, arg4, arg5);
        }

        ...    

        public override Task OnConnected()
        {
            _erp.SendOrderStatus();       
            return base.OnConnected();
        }
    }
}

All works fine: I can show data int the web page coming from the C# application (from the Erp class, and send commands from the web page back to the engine application. Here I reported only a small set of functions, but they should be enough to understand what happens.

Perhaps I'm blind and I cannot see my mistake looking at the examples. Anyway, every time I refresh a page in the browser or even I load another page of the same application (that of course shares the same JavaScript code above) I receive more and more SignalR messages! I mean, if the first time I receive the orderStatus message every 10 seconds, after a reload (or a page change) I receive 2 messages every 10 seconds. Another refresh and they become 3, and so on... after some time the whole system become unusable because it receives thousands of messages at once.

I'm aware there's the OnDisconnected() callback but it seems it's called by the framework to notify a client has disconnected (and I'm not interested to know it).

UPDATE

I cannot post the whole Erp class because it 3k+ lines long... anyway, most of the code does all other kind of stuff (database, field communications, etc...). Here the only functions involved with the hub:

public event Action<int, int, int, int, int> HubSendOrderStatus;

public void SendOrderStatus()
{
    using (MyDBContext context = new MyDBContext())
    {
        var openedOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Opened);
        var suspendedOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Suspended);
        var runningOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Running || x.State == OrderStates.Queued);
        var closedOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Completed);
        var cancelledOrders = context.Orders.AsNoTracking().Count(x => x.State == OrderStates.Cancelled);

        HubSendOrderStatus?.Invoke(openedOrders, suspendedOrders, runningOrders, closedOrders, cancelledOrders);
    }
}

public async Task ImportOrdersAsync()
{
    // doing something with I/O file
    SendOrderStatus();
}

public void JobImportOrders()
{
    Timer t = null;
    t = new Timer(
    async delegate (object state)
    {
        t.Dispose();
        await ImportOrdersAsync();
        JobImportOrders();
    }, null, 10 * 1000, -1);
}

public Erp()
{
    // initialize other stuff
    JobImportOrders();
}

EDIT

AutofacContainer.cs

public class AutofacContainer
{
    public IContainer Container { get; set; }
    public AutofacContainer()
    {
        var builder = new ContainerBuilder();
        var config = new HubConfiguration();
        builder.RegisterHubs(Assembly.GetExecutingAssembly()).PropertiesAutowired();
        builder.RegisterType<Erp>().PropertiesAutowired().InstancePerLifetimeScope();
        Container = builder.Build();
        config.Resolver = new AutofacDependencyResolver(Container);
    }
}

Solution

  • Following the suggestions of "Alex - Tin Le" I discovered the handlers weren't removed with:

    _erp.HubSendOrderStatus -= Erp_HubSendOrderStatus;
    

    I fixed this weird behavior checking if the handler is already registered:

    if (!_erp.HubSendOrderStatus_isRegistered())  _erp.HubSendOrderStatus += Erp_HubSendOrderStatus;
    

    The HubSendOrderStatus_isRegistered function is this:

    public bool HubSendOrderStatus_isRegistered()
    {
        return HubSendOrderStatus != null;
    }
    

    I solved the initial problem: no more flood of messages when making new connections.

    The last bit I don't understand is why it sends out 2 messages at time no matter the number of active connections. Debugging both the Javascript and the server code I noticed that a new connection is made on $.connection.hub.start().done(function () { }); as expected. But another was already created when refreshing a page, even before any available breakpoint. But removing the "explicit" one leads to receive no messages.