1

I am trying to send a list of objects to my signalr clients, but the framework seems to ignore the json serializer and uses the ToString method instead. This only happens if it's a List<object>, it can handle a regular object just fine.

A somewhat contrived simplified example:

public abstract class Animal {}
public class Dog : Animal {
    public bool GoodBoy { get; set; }
    public override string ToString() {
        return "hello from dog";
    }
}
public class Cat : Animal {
    public bool Hungry { get; set; } // only cats can be hungry, dont think too much about it
}

// ... class MyHub : Hub { ...

// Input is: type = typeof(Dog) and animals is a list of 1 dog
public void SendChanges(Type type, List<Animal> animals) {
    // Just serializing the animals without casting them gives incorrect objects
    Console.WriteLine(JsonSerializer.Serialize(animals)); // Prints [{}]

    // So we have to cast to the input type
    var animalsAsType = animals.Select(a => Convert.ChangeType(a, type).ToList();
    Console.WriteLine(JsonSerializer.Serialize(animalsAsType)); // Prints [{"GoodBoy":true}]

    // However, ...
    Clients.Others.SendAsync($"On{type.Name}Changed", animalsAsType); // Clients receive ["hello from dog"]
}

What am I doing wrong here? I suspect there's something wrong in my linq Select, but I am unsure how to cast an Animal to type (with it throwing a ClassCastException if the animal instance is not actually the type we're trying to cast to) that does not use Convert.ChangeType.

Note: Due to technical limitations in my real code I do require that the signature of SendChanges(Type type, List<Animal> animals) doesn't change.

Sillydan
  • 104
  • 13

2 Answers2

0

Instead of passing the type as parameter, have your tried making it a generic method instead? Then it knows the actual type during runtime so you shouldn't have to cast anything:

public void SendChanges<T>(List<T> updatedModels)
{
    Clients.Others.SendAsync($"On{typeof(T).Name}Changed", updatedModels);
}

would make your class a bit generic even. If you want to restrict it to animals, just add where T : Animal to the method signature.

Edit: Use it like this:

var cats = new List<Cat>() { new Cat() };
signalerService.SendChanges(cats);
Squirrelkiller
  • 2,575
  • 1
  • 22
  • 41
  • Thanks for your answer. However, I really _need_ to have a `Type` instance in my case. Also hub functions cannot be generic. – Sillydan Feb 01 '23 at 13:02
  • In that case you're probably better off using individual functions. In fact, you could add your client's interface to the Hub so you can call them directly instead of building the "OnCatChanged" name yourself, like here: https://gitlab.com/Regenhardt/Teambuilder/-/blob/master/Teambuilder%20WebAPI/ActiveAdapters/Hubs/TeambuilderHub.cs#L137 (Look at the class inheritance `Hub`) – Squirrelkiller Feb 01 '23 at 15:05
0

I found the issue: Turns out that I forgot that I had registered a custom json converter that used ToString. Once I followed this solution everything worked perfectly.

Sillydan
  • 104
  • 13