SignalR
This library provides a MessagePack-based Hub Protocol implementation for ASP.NET Core SignalR, offering significant performance improvements over the default JSON protocol.
Benefits
- Smaller Payloads: MessagePack produces significantly smaller payloads compared to JSON
- Faster Serialization: Binary serialization is typically faster than text-based formats
- Type Safety: Leverages MessagePack's type-safe serialization system
- NativeAOT Compatible: Works seamlessly with .NET Native AOT compilation
Installation
Install the NuGet package:
Add a call to AddMessagePackProtocol to your builder class, as demonstrated in the configuration samples below.
Configuration
SignalR being an RPC system requires a provider for type shapes for all parameter and return types used in your RPC methods. This can be ReflectionTypeShapeProvider.Default or (preferably) via the PolyType source generator using a witness class
The sample configurations below will be using the witness class approach.
[GenerateShapeFor<bool>] // add as many attributes as necessary for each RPC parameter and return type.
partial class Witness;
Server Configuration
Add the MessagePack protocol to your SignalR hub:
var builder = WebApplication.CreateBuilder(args);
// Add SignalR with MessagePack protocol
builder.Services.AddSignalR()
.AddMessagePackProtocol(Witness.GeneratedTypeShapeProvider);
var app = builder.Build();
// Configure hub endpoint
app.MapHub<ChatHub>("/chatHub");
Custom Serializer Configuration
You can provide a custom MessagePack serializer with specific configuration:
var customSerializer = new MessagePackSerializer
{
// Your custom configuration
};
builder.Services.AddSignalR()
.AddMessagePackProtocol(Witness.GeneratedTypeShapeProvider, customSerializer);
Client Configuration
For .NET SignalR clients, add the MessagePack protocol to your connection:
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chatHub")
.AddMessagePackProtocol(Witness.GeneratedTypeShapeProvider)
.Build();
await connection.StartAsync();
Client with Custom Serializer
var serializer = new MessagePackSerializer
{
// custom configuration
};
var connection = new HubConnectionBuilder()
.WithUrl("https://example.com/chatHub")
.AddMessagePackProtocol(Witness.GeneratedTypeShapeProvider, serializer)
.Build();
Supported Message Types
The MessagePack Hub Protocol supports all SignalR message types:
InvocationMessage: Method calls from client to serverStreamInvocationMessage: Streaming method callsCompletionMessage: Method call completionsStreamItemMessage: Individual stream itemsCancelInvocationMessage: Stream cancellationsPingMessage: Keep-alive pingsCloseMessage: Connection close notificationsAckMessage: AcknowledgementsSequenceMessage: Sequence messages
Hub Implementation
Your SignalR hubs work exactly the same way with the MessagePack protocol:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await this.Clients.All.SendAsync("ReceiveMessage", user, message);
}
public async IAsyncEnumerable<string> StreamData(
[EnumeratorCancellation] CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
yield return $"Data {i}";
await Task.Delay(1000, cancellationToken);
}
}
}
Performance Considerations
The MessagePack protocol generally provides better performance characteristics:
- Bandwidth: 20-50% smaller payload sizes compared to JSON
- CPU: Faster serialization/deserialization, especially for complex objects
- Memory: Lower memory allocation during serialization
Consider using MessagePack when:
- You have high-frequency message exchanges
- Your messages contain complex data structures
- Bandwidth is a concern (mobile applications, metered connections)
- You need maximum performance
Compatibility
The MessagePack Hub Protocol is compatible with:
- ASP.NET Core 8.0 and later
- .NET SignalR clients
- NativeAOT compilation
- All standard SignalR features (groups, user connections, etc.)
While SignalR comes with its own MessagePack implementation, it does so by relying on the MessagePack-CSharp library, which is harder to use and not NativeAOT safe. Nerdbank.MessagePack.SignalR provides a comparably high performance, easier to use and NativeAOT-safe alternative. It is compatible with other MessagePack implementations that follow the same protocol as prescribed by SignalR in their spec.
Migration from JSON Protocol
Migration is straightforward and requires minimal code changes:
- Install the
Nerdbank.MessagePack.SignalRpackage - Add .AddMessagePackProtocol to your SignalR registration
- Update clients to use the MessagePack protocol
- No changes needed to your Hub methods or client method calls
Both server and clients must use the same protocol for communication.
NativeAOT compatibility
This package is fully NativeAOT compatible, however depending on other code or dependencies in your application, you might see trim warnings like these:
ILC : Trim analysis warning IL2070: System.Windows.Input.CommandConverter.ConvertFromHelper(Type,String): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicProperties' in call to 'System.Type.GetProperty(String,BindingFlags)'. The parameter 'ownerType' of method 'System.Windows.Input.CommandConverter.ConvertFromHelper(Type,String)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
Nerdbank.MessagePack.SignalR has a transitive dependency on WPF through its Nerdbank.Streams dependency.
This normally trims entirely away, but code in your app or other dependencies you have might lead the trimmer to root parts of WPF, leading to warnings like the one above.
If your app doesn't use WPF, you can forcibly remove it from your application by adding this to your app's project file:
<Target Name="RemoveWPF" BeforeTargets="ResolveLockFileReferences">
<ItemGroup>
<FrameworkReference Remove="Microsoft.WindowsDesktop.App.WPF" />
</ItemGroup>
</Target>
Note this must be added to the final application's project file. It is not effective when adding it to some intermediate library project.
Learn more about this from this discussion.