Dan Hastings
Dan Hastings

Reputation: 3290

Unable to track connected users using Signalr hub class

I have created a class that inherits the Signalr Hub class and it runs on startup. When a connection is made, there are some custom headers sent from the client that I use to generate a user object. I want to store these in memory on the server so that I can retrieve the list and display them in a UI. Someone can then use this UI to see the user info and perform interactions with this connection. I have setup a hub class in an ASP MVC project and i am using a console app for the client. I can connect fine and the server can communicate back, but the property that I use in the hub class to keep track of the connected users is reset to null every time a request is made to the hub class.

public class JobRunHandler : Hub
{
    private List<JobRunClient> RunningJobs { get; set; }

    public JobRunHandler()
    {
        if(this.RunningJobs == null) this.RunningJobs = new List<JobRunClient>();
}

public override Task OnConnected()
{
    JobRunClient runclient = new JobRunClient()
    {
        ConnectionID = Context.ConnectionId,
        Someotherstuff = this.GetHeaderInt(Context.Headers["Someotherstuff"])
    };

    this.RunningJobs.Add(runclient);
    return base.OnConnected();
}

public override Task OnReconnected()
{
    var existingClient = this.GetConnectingClient();
    if (existingClient == null)
    {
        JobRunClient runclient = new JobRunClient()
        {
            ConnectionID = Context.ConnectionId,
            Someotherstuff = this.GetHeaderInt(Context.Headers["Someotherstuff"])
        };

        this.RunningJobs.Add(runclient);
    }
    return base.OnReconnected();
}

public override Task OnDisconnected(bool stopCalled)
{
    this.RemoveClient(Context.ConnectionId);
    return base.OnDisconnected(stopCalled);
}

public void TestMethod()
{
    Clients.All.ping();
    var client = this.GetConnectingClient();
}

}

I have put break points in every method so i know when it runs. The client never disconnects or triggers reconnect, so there is no issue with the connection being broken. The client connects and the OnConnected() method triggers and the value is added to this.RunningJobs. The client then calls the TestMethod() which works, but when i check this.RunningJobs it is empty.

When Clients.All.ping(); runs it does actually send a ping back to the client. So the connection is made successfully, the server maintains the connection and i can send a ping back to the client when a separate method is called, but for some reason the property is being reset and I dont know why. I can use redis for this if I have to, but I have seen others use this strategy and its not been an issue.

Here is the client code I have created to test this (the console app)

    var hubConnection = new HubConnection("http://localhost:2497/");
    hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
    IHubProxy myHubProxy = hubConnection.CreateHubProxy("JobRunHandler");

    myHubProxy.On("ping", () => Console.Write("Recieved ping \n"));
    hubConnection.Headers.Add("Someotherstuff", "1");
    hubConnection.Start().Wait();


    while(true)
    {
        myHubProxy.Invoke("BroadcastCompletion").ContinueWith(task =>
        {
            if (task.IsFaulted)
            {
                Console.WriteLine("!!! There was an error opening the connection:{0} \n", task.Exception.GetBaseException());
            }

        }).Wait();
        Console.WriteLine("Broadcast sent to the server.\n");
        Thread.Sleep(4000);
    }

Upvotes: 0

Views: 640

Answers (2)

Dan Hastings
Dan Hastings

Reputation: 3290

I changed the property being used for this to a ConcurrentDictionary and this seems to be doing the trick. It allows me to store the client connections across all connections. I used the following code for this.

private static readonly ConcurrentDictionary<string, JobRunClient> RunningJobs = new ConcurrentDictionary<string, JobRunClient>();

Upvotes: 0

Pawel
Pawel

Reputation: 31620

The hub is transient. SignalR creates a hub instance each time a hub method is invoked so you cannot store any state in an instance property between the calls.

Upvotes: 1

Related Questions