Tiny Difference, Big Consequences Reloaded: SignalR in .NET Core 3.1 vs. .NET 5

In a past blog post I wrote about a surprising change I encountered during the migration of a .NET Core 2.2 application to .NET Core 3.0. I have just migrated that same application to .NET 5 and guess what, I stumbled across another “tiny difference with big consequences”.

The situation: I have a WPF desktop client that communicates with an ASP.NET Core server that in turn communicates with a small TypeScript browser application. I use Web API for accessing files and meta data, and SignalR to call remote functions.

After the migration I ran the application and things were working fine – until a certain point, where I received the following exception in a call of SignalR’s HubConnection.InvokeCoreAsync() (actual function name replaced with “doSomething”):

Microsoft.AspNetCore.SignalR.HubException
   HResult=0x80131500
   Message=Failed to invoke 'doSomething' due to an error on the server.
   Source=System.Private.CoreLib

On the server side, my own code was not even called.

It took me a while to notice that one of the parameters of the function had the type of a class with a specific constellation of constructors:

  • A public constructor with parameters
  • A private default constructor.

It turned out while SignalR in .NET Core 3.1 has no problem (de)serializing an instance of a class with a private default constructor, SignalR in .NET 5 does. So simply removing that constructor fixed the problem. In terms forcing the usage of the other constructor when explicitly creating objects of that type, nothing changed; I did not need a private default constructor for that.

P.S. Do not ask me why I had a private default constructor in the first place. But the project is a couple of years old now, and things like this do happen.

4 Comments

  • I guess it can be related to the JSON serializer: you're probably now using System.Text.Json instead of Newtonsoft.Json. Can you try to replace it and see if it works?

  • Not directly SignalR-related, but speaking of JSON changes: When Web API moved from Newtonsoft.Json in .NET Core 2.2 to System.Text.Json in .NET Core 3.0, I read about the possibility to keep using Newtonsoft.Json, but I figured I'd better make things work for me using the default (that led to the blog post https://weblogs.asp.net/rweigelt/json-serialization-in-net-core-3-tiny-difference-big-consequences). So I got rid of the Newtonsoft.Json dependency for network-related serialization in .NET Core 3.0. The move to 3.1 didn't have a noticeable impact.
    Back to SignalR: From skimming the documentation, my guess for the cause of the current breaking change would be the move from MessagePack 1.x in .NET Core 3.1 to MessagePack 2.x in .NET 5 mentioned here: https://docs.microsoft.com/de-de/dotnet/core/compatibility/3.1-5.0#signalr-messagepack-hub-protocol-moved-to-messagepack-2x-package

  • a related problem... if a Hub method returns null, S.T.Json will throw (a documented breaking change in STJ). The connection then aborts.
    A fix is in "master". Annoyingly almost the very next commit after the release was tagged.
    Hopefully there will be a 5.0.1 v soon.
    Until then, never allow a Hub method to return null.

  • @Andy: Good to know!

Comments have been disabled for this content.