Search code examples
javascriptc#asp.net-coreasp.net-core-signalr

Hub Method only running once


(I'm new to Signalr)

I'm developing an web application which uses Signalr-core to update the page in realtime.

The problem i walk into is that when i run multiple clients the method i'm running wil run as many times at once as there are clients.

so i want to know if there is any way to make the first client call the hub method and then keep it running and broadcasting to all connected clients.

this is what i'm doing with my client:

connection.on('update', (id, Color) => {
var x = document.getElementById(id);
if (Color === "green" && x.classList.contains("red"))
{            
    //console.log("green");
    x.classList.remove("red");
    x.classList.add("green");
}            
else if (Color === "red" && x.classList.contains("green"))
{            
    //console.log("Red");
    x.classList.remove("green");
    x.classList.add("red");
}});

connection.start()
.then(() => connection.invoke('updateclient', updating));

and this is what i'm doing with my hub:

 public void UpdateClient(bool updating)//this must run only once
    {
        while (updating == true)
        {
            Thread.Sleep(2000);
            foreach (var item in _context.Devices)
            {

                IPHostEntry hostEntry = Dns.GetHostEntry(item.DeviceName);
                IPAddress[] ips = hostEntry.AddressList;
                foreach (IPAddress Ip in ips)
                {
                    Ping MyPing = new Ping();

                    PingReply reply = MyPing.Send(Ip, 500);
                    if (reply.Status == IPStatus.Success)
                    {
                        Color = "green";
                        Clients.All.InvokeAsync("update", item.DeviceID, Color);

                        break;
                    }
                    else
                    {
                        Color = "red";
                        Clients.All.InvokeAsync("update", item.DeviceID, Color);
                    }
                }

            }

        }
    }

please let me know if i'm unclear about something.

and thank you in advance.


Solution

  • As I mention in the comment, you can invoke UpdateClient method on first appearance of any client. The option I came up with is as follows;

    It is quite common to use a static list for clients connected to hub

     public static class UserHandler
     {
         public static List<string> UserList = new List<string>();
     }
    

    In your hub, override OnConnected and OnDisconnected methods to add/remove clients to/from the list and define the very first client connected to hub for your purpose.

    public class MyHub : Hub
    {
        private static IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
    
        public override Task OnConnected()
        {      
            //Here check the list if this is going to be our first client and if so call your method, next time because our list is filled your method won't be invoked.
            if(UserHandler.UserList.Count==0)
                UpdateClient(true);
    
            UserHandler.UserList.Add(Context.ConnectionId)     
            return base.OnConnected();
        }
        public override Task OnDisconnected(bool stopCalled)
        {   
            UserHandler.UserList.RemoveAll(u => u.ConnectionId == Context.ConnectionId);
            return base.OnDisconnected(stopCalled);
        }
    }
    

    I didn't take your business logic or your needings into consideration, this is just a general idea. For example, you might want to add some logic for when all the clients goes off, you should stop the loop inside UpdateClient with using updating flag.