Jacob JA Shanks
Jacob JA Shanks

Reputation: 369

Unable to return a value from SignalR Client from a different method

I'm working on a Winforms app that executes SQL Procedures through a SignalR client. I'm relatively new to using SignalR and am still wrapping my head around it.

I start off by running my connection method to establish a connection with my SignalR service. I have two addresses configured ready for when I puslish but the DEV configuration leads to the SignalR service I am hosting locally.

Connection to SignalR (ConnectHub)

private async Task ConnectHub()
        {
            string hubAddress = "";
#if DEBUG
            HubAddress = ConfigurationManager.AppSettings["HubAddress_DEV"];
#else
            HubAddress = ConfigurationManager.AppSettings["HubAddress_PROD"];
#endif

            if (string.IsNullOrEmpty(hubAddress))
            {
                MessageBox.Show("Hub Address is missing from configuration.");
            }

            ConnectionHandler.Client = new HubClient(hubAddress, "MyHub");

            ConnectionHandler.Client.MyAlert += ConnectionHandler.ClientOnMyAlert;

            ConnectionHandler.Client.ServerErrorEvent += ConnectionHandler.ClientOnServerErrorEvent;

            await ConnectionHandler.Client.Connect(new List<string>() {
                VehicleInfo.ThisVehicle.WarehouseCode,
                VehicleInfo.ThisVehicle.VehicleName
            });
        }

My client is stored globally in my ConnectionHandler class where my event handlers are also kept. (I have breakpoints on these as I have not implemented them yet)

ConnectionHandler Class

public static class ConnectionHandler
    {
        public static HubClient Client { get; set; }

        public static void ClientOnServerErrorEvent(string error)
        {
            throw new NotImplementedException(); //Currently not implemented
        }

        public static async Task ClientOnMyAlert(EnumMyAlertType alerttype, string message, Exception exception)
        {
            await Task.Yield(); //Currently not implemented
        }
    }

When I call the code to Invoke the procedure in my SignalR client, it returns a DataTable to me which is the intended result. Call to SignalR

await ConnectHub();

DataTable dt = await ConnectionHandler.Client.Connection.InvokeCoreAsync<DataTable>(
    "FetchStatuses",
    new object[0]); //This call works as intended and returns a populated DataTable

StatusInfo = new CStatuses();

All the above code is currently done on the main form, however I wanted to move this call to SignalR into a constructor to try and tidy things up.

The problem comes when I try to move this call into another method, the program hangs as I don't think it has received the return value from SignalR, I have placed a breakpoint beneath it and it is not reached. A TryCatch reveals nothing as it hangs within the "Try" with no exception.

Calling from contructor

public CStatuses()
        {
            Statuses = new List<CStatus>();

            var dataTable = ConnectionHandler.Client.Connection.InvokeCoreAsync<DataTable>("FetchStatuses",
                    new object[0])
                .Result; //My program hangs on this line and proceeds no further

I am at a loss as to why it is doing this when I can get a value from the client from the form and when other members of my team have tried to do the same thing they can make a call to SignalR also from a different method.

Does anyone have any ideas as to how I can make this work?

I realize this has gotten quite long but if I can elaborate on things please let me know


FIXED CODE THANKS TO SOLUTION: I have moved the code from my CStatuses constructor into a new async method within the same class and called it after initialization. This removes the need for .Result and appears to solve the problem for me.

public async Task PopulateStatuses()
        {
            var dataTable = await ConnectionHandler.Client.Connection.InvokeCoreAsync<DataTable>("FetchStatuses",
                new object[0]);

            Statuses = new List<CStatus>();

            foreach (DataRow row in dataTable.Rows)
            {
                var status = new CStatus
                {
                    StatusId = Common.Utility.GetInt16Value(row["StatusID"]),
                    StatusCode = Common.Utility.GetStringValue(row["StatusCode"]),
                    Description = Common.Utility.GetStringValue(row["Description"])
                };

                Statuses.Add(status);
            }
        }

Upvotes: 1

Views: 860

Answers (1)

Tiago Silva
Tiago Silva

Reputation: 2349

You are running into a deadlock with the .Result call, I would suggest creating an async method in the CStatuses class and after you initialize your CStatuses class call the websocket for data.

Upvotes: 1

Related Questions