SignalRClientGenerator 0.0.7

dotnet add package SignalRClientGenerator --version 0.0.7
                    
NuGet\Install-Package SignalRClientGenerator -Version 0.0.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SignalRClientGenerator" Version="0.0.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SignalRClientGenerator" Version="0.0.7" />
                    
Directory.Packages.props
<PackageReference Include="SignalRClientGenerator" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SignalRClientGenerator --version 0.0.7
                    
#r "nuget: SignalRClientGenerator, 0.0.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SignalRClientGenerator@0.0.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SignalRClientGenerator&version=0.0.7
                    
Install as a Cake Addin
#tool nuget:?package=SignalRClientGenerator&version=0.0.7
                    
Install as a Cake Tool

®️ SignalRClientGenerator

SignalRClientGenerator on NuGet Gallery

Automatically generate a strongly-typed .NET client for a SignalR server based on shared interfaces.

  1. Background
  2. Problem
  3. Solution
    1. Server side
    2. Client side
  4. Sample

Background

SignalR is a real-time message broker that resembles Socket.IO. It handles connection, reconnection, multiple transports (including WebSockets) and fallbacks, authentication and authorization, marshalling, queues and consumers, and RPC reply correlation semantics. It provides a server either in-process with ASP.NET Core apps or out-of-process hosted in Azure, and client libraries in .NET, Java, Javascript, and Swift.

Without SignalR, using raw WebSockets is very limited. There is no way to tell which clients are connected at any given time, handle disconnection events, send messages to only a subset of clients, frame messages that span multiple packets, serialize and deserialize types, or correlate a response to its request. You can write all that yourself, which is educational, but after doing it at most once you will want to use SignalR in the future.

Problem

The SignalR server already allows type-safe events using strongly-typed hubs. However, the client (including the .NET client, which is written in the same language as the server) does not have any built-in options for event type safety. All events received and sent from the client are weakly-typed using string names for methods and ad-hoc parameter and return types.

This means that if you change a method's name, parameters, or return type, these changes won't flow from the server to the client codebases. The out-of-date client code will continue to compile with its incorrect strings, and will either crash or silently ignore events at runtime. This is made worse by the fact that important SignalR marshalling errors are logged as debug messages in the same class as lots of other unimportant debug messages, so they are very hard to spot and too verbose to leave on.

Even without the safety of strong types, having code completion makes development much simpler, faster, easier, and less annoying. APIs become more self-documenting, and an entire class of problems with incorrect or unknown signatures is eliminated.

Solution

Server side

Strongly-typed hubs are already available built-in to ASP.NET Core SignalR, and they do not use this package. The steps to implement them are repeated here as a reminder, and because the client-side usage depends on the example shared code defined here. To see the novel client-side autogeneration, skip to the Client side section.

To use strongly-typed server-side hubs, you define an interface for the events sent from server to client, and specify that interface as a generic type parameter for the Hub<TClient> that you subclass, as well as any IHubContext<THub, TClient> that you inject. To define the signatures of the events from the client to the server, you make your Hub<TClient> implement the interface of events from the client, and you implement those methods in your hub subclass.

The names, parameter types, and return types of both these sets of methods in the hub and client interfaces provide server-side type safety for the messages sent to and from the server.

Example
Share event definitions in interfaces

These interfaces should be extracted to a shared library which is depended upon by both the server and client projects. These can also inherit from superinterfaces.

public interface EventsToClient {

    Task helloFromServer(DateTimeOffset currentTime);

}

public interface EventsToServer {

    Task helloFromClient(string name);

}
Receive messages on server

Subclass the Hub<TClient> abstract class, parameterizing it with the interface of outgoing events, and also implement the interface of incoming events.

public class SampleHub(ILogger<SampleHub> logger): Hub<EventsToClient>, EventsToServer {

    public async Task helloFromClient(string name) {
        logger.LogInformation("{name} said hello", name);
    }

}
Send messages from server

Inject an IHubContext<THub, TClient> parameterized with the Hub<TClient> you subclassed and the outgoing events interface.

public class Greeter(IHubContext<SampleHub, EventsToClient> hub, ILogger<Greeter> logger): BackgroundService {

    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        while (!stoppingToken.IsCancellationRequested) {
            await hub.Clients.All.helloFromServer(DateTimeOffset.Now);
            logger.LogInformation("Sent hello to all clients");
            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }
    }

}

Client side

Normally, you would have to manually call HubConnection.On and pass the name of helloFromServer as a string, or at least use nameof(EventsToClient.helloFromServer) to allow only the name to be refactored.

By using a source generator, this package transforms the shared interfaces into a strongly-typed wrapper for your client-side HubConnection. Any time you add, change, or remove an event that is sent to or from the client, it will regenerate the strongly-typed client. This means that a new or changed parameter, changed type, or renamed method will immediately cause an obvious compiler error without a developer having to remember to do anything, instead of waiting to stealthily fail at runtime. It also makes it easier to develop clients because you don't have to manually retype every method signature, they're just provided for you with code completion.

Usage
  1. Declare a dependency from your SignalR client project to this generator package.
    dotnet add package Microsoft.AspNetCore.SignalR.Client
    dotnet add package SignalRClientGenerator
    
  2. Create a new empty partial class that will become the strongly-typed client.
    public partial class SampleClient;
    
  3. Annotate the class with SignalRClientGenerator.GenerateSignalRClientAttribute, specifying zero or more interfaces that represent the incoming and outgoing events.
    [GenerateSignalRClient(Incoming = [typeof(EventsToClient)], Outgoing = [typeof(EventsToServer)])]
    public partial class SampleClient;
    
  4. Construct a new instance of your client class, passing your HubConnection as an argument.
    await using HubConnection hub = new HubConnectionBuilder().WithUrl("http://localhost/events").Build();
    SampleClient client = new(hub);
    
  5. Connect the hub to the server as with a normal SignalR client.
    await hub.StartAsync(cancellationToken);
    
Receive incoming events in client

Subscribe to the autogenerated event on your client class, instead of calling hub.On("helloFromServer", (DateTimeOffset time) => {}).

client.helloFromServer += async (sender, time) => Console.WriteLine($"It is currently {time}");
Send outgoing events from client

Call the autogenerated method on your client class, instead of calling hub.InvokeAsync("helloFromClient", Environment.UserName).

await client.helloFromClient(Environment.UserName, cancellationToken);

Sample

Check out the sample server, client, and shared library projects in this repository for a complete example that compiles and runs.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.7 153 11/23/2025
0.0.6 390 11/17/2025
0.0.5 188 10/21/2025
0.0.4 178 10/21/2025
0.0.3 135 8/16/2025
0.0.2 175 6/30/2025
0.0.1 167 6/30/2025
0.0.0 178 6/30/2025