ASP.NET Core 2 SignalR Hub to receive complex object instead of string
Asked Answered
L

4

6

I'm currently working on a ASP.NET Core 2 application using SignalR Core. I was wondering if it is possible to receive a complex object within the Hub class instead of a simple string or simple data structure.

Works - This example works fine: string message

public class MyHub : Hub
{
    public Task SendMessage(string message)
    {
        // ... some logic
    }
}

Works - This example works fine as well: List<Dictionary<string, object>> message

public class MyHub : Hub
{
    public Task SendMessage(List<Dictionary<string, object>> message)
    {
        // ... some logic
    }
}

Doesn't work correctly - It seems I cannot transfer complex objects via SignalR e.g. if I create a custom message class:

public class Message 
{
   public int MessageId { get; set; }

   public List<Dictionary<string, object>> Items { get; set; }

   public List<string> TextMessages { get; set; }
}

public class MyHub : Hub
{
    public Task SendMessage(Message message)
    {
        // ... some logic
    }
}

Do you know how to transfer complex objects via a SignalR RPC?

Thank you!

Likeminded answered 14/1, 2019 at 8:16 Comment(4)
Can you add the client side code as well for the hub where SendMessage accepts Message parameter? As long as you are sending in right JSON, it should go through.Elect
Thanks, I'm working in .NET only, no JavaScript though... perhaps I'm just missing the [Serializable] attribute...Likeminded
Share us your signalR client code.Huppah
I thought SignalR would automatically serialize your complex object into JSON for you, and then deserialize the JSON back to your complex object. I take it this is NOT correct?Eaglestone
H
1

Follow steps below for a working demo which passing Message between signalR Client and Server.

  1. Server

    public class TimeHub: Hub
    {
        public async Task UpdateTime(string message)
        {
            if (Clients != null)
            {
                await Clients?.All.SendAsync("ReceiveMessage", message);
            }
        }
        public Task SendMessage(Message message)
        {
            // ... some logic
            return Task.CompletedTask;
        }
    }
    
  2. Client

    private static async void Connect()
    {
        var hubConnectionBuilder = new HubConnectionBuilder();
        #region Worked
        var hubConnection = hubConnectionBuilder.WithUrl("https://localhost:44381/timeHub", options =>
        {
    
        }).Build();
        #endregion
    
        await hubConnection.StartAsync();
        await hubConnection.SendAsync("UpdateTime", $"From Client");
        var item1 = new Dictionary<string, object> {
            { "T1", new { Name = "TT1" } },
            { "T2", new { Name = "TT2" } },
            { "T3", new { Name = "TT3" } },
        };
        var item2 = new Dictionary<string, object> {
            { "T11", new { Name = "TT11" } },
            { "T12", new { Name = "TT12" } },
            { "T13", new { Name = "TT13" } },
        };
    
        await hubConnection.SendAsync("SendMessage", new Message {
            MessageId = 1,
            Items = new List<Dictionary<string, object>> {
                item1,
                item2
            },
            TextMessages = new List<string> {
                "H1",
                "H2"
            }
        });
        var on = hubConnection.On("ReceiveMessage", OnReceivedAction);
        Console.WriteLine($"Client is Start");
        Console.ReadLine();
        on.Dispose();
        await hubConnection.StopAsync();
    }
    
Huppah answered 15/1, 2019 at 3:28 Comment(0)
C
7

You can use the Newtonsoft.Json Nuget.

There you have a JsonConverter that can serializ your object.

So in your example:

    public class MyHub : Hub
    {
          public Task SendMessage(Message message)
          {
             var messageJsonString = JsonConvert.SerializeObject<Message>(message);
             // some logic
           }
    }

And on your client you can convert it back to an object. It´s have a nativ API so you just call

connection.on("ReceiveMessage",  (message) => { 
    let messageObject = JSON.parse(message);
    // Other code here
});

Now message is again the object you send from the server.

And of course you can use JsonConvert.DeserializeObject<T>() to convert a json string you recieve from the client into a Object.

Caius answered 14/1, 2019 at 10:19 Comment(1)
Excellent answer - going through the annoyance of how MS decided to handle serialize/deserialize operations is aggravating. A nice simple - do it yourself solution like this helps alleviate headaches!Jaime
H
1

Follow steps below for a working demo which passing Message between signalR Client and Server.

  1. Server

    public class TimeHub: Hub
    {
        public async Task UpdateTime(string message)
        {
            if (Clients != null)
            {
                await Clients?.All.SendAsync("ReceiveMessage", message);
            }
        }
        public Task SendMessage(Message message)
        {
            // ... some logic
            return Task.CompletedTask;
        }
    }
    
  2. Client

    private static async void Connect()
    {
        var hubConnectionBuilder = new HubConnectionBuilder();
        #region Worked
        var hubConnection = hubConnectionBuilder.WithUrl("https://localhost:44381/timeHub", options =>
        {
    
        }).Build();
        #endregion
    
        await hubConnection.StartAsync();
        await hubConnection.SendAsync("UpdateTime", $"From Client");
        var item1 = new Dictionary<string, object> {
            { "T1", new { Name = "TT1" } },
            { "T2", new { Name = "TT2" } },
            { "T3", new { Name = "TT3" } },
        };
        var item2 = new Dictionary<string, object> {
            { "T11", new { Name = "TT11" } },
            { "T12", new { Name = "TT12" } },
            { "T13", new { Name = "TT13" } },
        };
    
        await hubConnection.SendAsync("SendMessage", new Message {
            MessageId = 1,
            Items = new List<Dictionary<string, object>> {
                item1,
                item2
            },
            TextMessages = new List<string> {
                "H1",
                "H2"
            }
        });
        var on = hubConnection.On("ReceiveMessage", OnReceivedAction);
        Console.WriteLine($"Client is Start");
        Console.ReadLine();
        on.Dispose();
        await hubConnection.StopAsync();
    }
    
Huppah answered 15/1, 2019 at 3:28 Comment(0)
C
1

If you are considering using JSON parsing just for the sake of passing multiple objects/parameters to client side, there is an alternative.

Server side (C#). You can pass any number of parameters to the anonymous object array.

SendCoreAsync("MethodName", new object[] {someObject, someNumber, someString });

Client side (Typescript)

private alertHandler = (someObject: any, someNumber: number, someString: string) => {
    console.log(someObject, someNumber, someString);
};

I have a slightly more detailed answer here

Clown answered 20/1, 2020 at 3:54 Comment(0)
D
0

In my case, I was receiving a custom class on the hub method, but the properties of that class had no setters, so all properties were default values.

So each property needs { get; set; } to be assigned during deserialization.

Discriminator answered 31/3 at 6:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.