Annotate SignalR server for native AOT
Fix SignalR Server's usage of MakeGenericMethod when using a streaming reader (IAsyncEnumerable or ChannelReader) following the same approach as the client. Add a runtime check and throw an exception when trying to stream a ValueType in native AOT.
Adjust the public annotations:
- Remove RequiresUnreferencedCode from AddSignalR
- Add RequiresUnreferencedCode to MessagePack and NewtonsoftJson protocols
Support trimming and AOT in DefaultHubDispatcher by adding a feature switch to turn off custom awaitable support. Update ObjectMethodExecutor to support trimming and AOT by a new method that doesn't look for custom awaitables, and uses reflection instead of Linq.Expressions. This will need a corresponding PR in https://github.com/dotnet/sdk to default the feature switch value when PublishTrimmed or PublishAot is set to true.
FYI - @agocke @MichalStrehovsky @sbomer - in case you want to take a look.
Do you have a size comparison?
Taking a dotnet new webapiaot project and publishing it for win-x64 on my machine, the resulting .exe is 8.73 MB (9,163,776 bytes).
Then adding the following to the app:
Program.cs
var builder = WebApplication.CreateSlimBuilder(args);
...
+builder.Services.AddSignalR();
+builder.Services.Configure<JsonHubProtocolOptions>(o =>
+{
+ o.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
+});
var app = builder.Build();
...
+app.MapHub<ChatHub>("/chathub");
app.Run();
ChatHub.cs
using Microsoft.AspNetCore.SignalR;
public class ChatHub(ILogger<ChatHub> logger) : Hub
{
public async Task SendMessage(string user, string message)
{
logger.LogInformation("Received message from {user}: {message}", user, message);
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Publishing again, the resulting .exe is 10.9 MB (11,445,760 bytes).
So roughly 2.2 MB of additional size to add SignalR server. Doing a really quick sizoscope on the before and after, here are my thoughts on what could be eliminated if we wanted:
-
System.Text.RegularExpressionsis getting pulled in, which is adding a lot of the size we cut out of the "Slim" builder. The reason it gets pulled in is because this callsAddRoutingand notAddRoutingCore. https://github.com/dotnet/aspnetcore/blob/0da8ea72b5434cbe8e1207d802f2270ca2f8ad4c/src/SignalR/common/Http.Connections/src/ConnectionsDependencyInjectionExtensions.cs#L21-L23 -
Super minor would be to remove the Linq usage in https://github.com/dotnet/aspnetcore/blob/0da8ea72b5434cbe8e1207d802f2270ca2f8ad4c/src/SignalR/server/Core/src/Internal/HubReflectionHelper.cs#L13 and https://github.com/dotnet/aspnetcore/blob/0da8ea72b5434cbe8e1207d802f2270ca2f8ad4c/src/SignalR/server/Core/src/Internal/HubMethodDescriptor.cs#L63
I think we are in a good spot right now though. I opened an issue for the Regex change, since that would have the biggest benefit.
I was more asking how much this PR saves vs. not trimming SignalR.
I was more asking how much this PR saves vs. not trimming SignalR.
If I take the same project as above and make the following changes to the .csproj:
<!-- <PublishAot>true</PublishAot> -->
<PublishSingleFile>true</PublishSingleFile>
The resulting .exe is 90.1 MB.