![]() |
VOOZH | about |
dotnet add package SignalRGen --version 2.1.2
NuGet\Install-Package SignalRGen -Version 2.1.2
<PackageReference Include="SignalRGen" Version="2.1.2" />
<PackageVersion Include="SignalRGen" Version="2.1.2" />Directory.Packages.props
<PackageReference Include="SignalRGen" />Project file
paket add SignalRGen --version 2.1.2
#r "nuget: SignalRGen, 2.1.2"
#:package SignalRGen@2.1.2
#addin nuget:?package=SignalRGen&version=2.1.2Install as a Cake Addin
#tool nuget:?package=SignalRGen&version=2.1.2Install as a Cake Tool
👁 Pipeline
👁 Static Badge
👁 NuGet
A source generator for SignalR that eliminates boilerplate and provides strongly-typed clients
SignalRGen transforms the SignalR experience by using source generation to:
dotnet add package SignalRGen
YOLO development builds are also available in this GitHub repository.
using SignalRGen.Abstractions;
using SignalRGen.Abstractions.Attributes;
[HubClient(HubUri = "chat")]
public interface IChatHubContract : IBidirectionalHub<IChatHubServerToClient, IChatHubClientToServer>
{
}
// This interface describes the flow of data from the server to the client.
public interface IChatHubServerToClient
{
Task MessageReceived(string user, string message);
}
// This interface describes the flow of data from the client to the server.
public interface IChatHubClientToServer
{
Task SendMessage(string message);
}
var app = builder.Build();
app.MapHub<ChatHub>($"{ChatHubContractClient.HubUri}");
builder.Services
.AddSignalRHubs(c => c.HubBaseUri = new Uri("http://localhost:5155"))
.WithChatHubContractClient();
public class ChatService
{
private readonly ChatHubContractClient _hubClient;
public ExampleComponent(ChatHubContractClient hubClient)
{
_hubClient = hubClient;
_hubClient.MessageReceived += HandleMessageReceived;
}
public async Task Initialize()
{
// Connection handling is done automatically
await _hubClient.StartAsync();
// Strongly-typed method invocation
await _hubClient.InvokeSendMessageAsync("Hello from SignalRGen!");
}
private async Task HandleMessageReceived(string user, string message)
{
Console.WriteLine($"New message from {user}: {message}");
return Task.CompletedTask;
}
}
SignalRGen uses C# source generators to analyze your SignalR hub interfaces and automatically generate:
@page "/Chat"
@using SignalRGen.Example.Contracts
@* We inject the generated client. *@
@* Notice that we use the actual type, not the interface we defined. *@
@inject ChatHubContractClient ChatHubClient;
<h3>Chat</h3>
<p>This is a really crude example of a chat that uses SignalR generated by SignalRGen to communicate.</p>
<p>It is kept simple to better showcase the generated SignalR code. This is not how a real chat app would look like.</p>
<p>To get the example working it is best to duplicate this tab a couple of times and open them in different browser tabs.
After that enter a username in each tab and join the chat room. You should be able to see the sent message on all tabs.</p>
@if (!_joined)
{
<div class="username-section">
<input type="text" @bind="_username" placeholder="Enter username"/>
<button @onclick="Join" disabled="@(!string.IsNullOrEmpty(_confirmedUsername))">Join</button>
</div>
}
else
{
<div class="chatting-as-section">
<p>Chatting as: <span>@_confirmedUsername</span></p>
</div>
<div class="message-section">
<input type="text" @bind="_message" placeholder="Enter message"/>
<button @onclick="SendMessage">Send</button>
<div class="messages">
@foreach (var message in _messages)
{
<div>@message</div>
}
</div>
</div>
}
@code {
private string _username = string.Empty;
private string _confirmedUsername = string.Empty;
private bool _joined = false;
private string _message = string.Empty;
private List<string> _messages = new();
private async Task Join()
{
if (!string.IsNullOrWhiteSpace(_username))
{
_confirmedUsername = _username;
_username = string.Empty;
_joined = true;
// We subscribe to the `OnUserJoined` event to get notified whenever a new user joined.
ChatHubClient.OnUserJoined += async user =>
{
_messages.Add($"~~ User '{user}' joined ~~");
await InvokeAsync(StateHasChanged);
};
// We subscribe to the `OnUserLeft` event to get notified whenever a user left.
ChatHubClient.OnUserLeft += async user =>
{
_messages.Add($"~~ User '{user}' left ~~");
await InvokeAsync(StateHasChanged);
};
// We subscribe to the `OnMessageReceived` event to get notified whenever a new message
// was sent by other users.
ChatHubClient.OnMessageReceived += async (user, message) =>
{
_messages.Add($"{user}: {message}");
await InvokeAsync(StateHasChanged);
};
// We use the headers in this stripped-down example as a means to send the username along.
// In a real chat app this would be dealt with differently, but it is a nice showcase
// on how to pass along runtime-based headers.
var headers = new Dictionary<string, string>()
{
{ "username", _confirmedUsername }
};
// We start the Hub, which will internally build up the connection to the server.
await ChatHubClient.StartAsync(headers: headers);
}
}
private async Task SendMessage()
{
// We are invoking the `InvokeSendMessageAsync` method to send the message to the server,
// which will propagate it to the other users.
await ChatHubClient.InvokeSendMessageAsync(_message);
_messages.Add($"{_confirmedUsername}: {_message}");
await InvokeAsync(StateHasChanged);
_message = string.Empty;
}
}
SignalRGen allows full customization of your hub connections:
builder.Services
.AddExampleHubs(c => c.HubBaseUri = new Uri("http://localhost:5155"))
.WithChatHubContractClient(config =>
{
config.HubClientLifetime = ServiceLifetime.Scoped;
config.HttpConnectionOptionsConfiguration = options =>
{
options.Headers.Add("custom-header", "foo");
};
config.HubConnectionBuilderConfiguration = connectionBuilder =>
{
connectionBuilder.WithKeepAliveInterval(TimeSpan.FromMinutes(1));
};
});
For complete documentation, samples, and API reference, visit our documentation site.
This library wouldn't be possible without the following people:
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 2.1.2 | 111 | 4/7/2026 | |
| 2.1.1 | 112 | 3/26/2026 | |
| 2.1.1-rc.1 | 65 | 3/24/2026 | |
| 2.1.0 | 131 | 1/21/2026 | |
| 2.1.0-rc.1 | 78 | 1/19/2026 | |
| 2.1.0-alpha.5 | 165 | 12/22/2025 | |
| 2.1.0-alpha.4 | 149 | 12/21/2025 | |
| 2.1.0-alpha.3 | 446 | 12/18/2025 | 2.1.0-alpha.3 is deprecated because it has critical bugs. |
| 2.1.0-alpha.2 | 253 | 12/16/2025 | |
| 2.1.0-alpha.1 | 129 | 10/17/2025 | |
| 2.0.0 | 223 | 8/20/2025 | |
| 2.0.0-alpha.4 | 212 | 8/8/2025 | |
| 2.0.0-alpha.3 | 173 | 7/16/2025 | |
| 2.0.0-alpha.2 | 180 | 7/16/2025 | |
| 2.0.0-alpha.1 | 181 | 7/9/2025 | |
| 1.0.0 | 286 | 6/1/2025 | |
| 1.0.0-rc.1 | 154 | 5/31/2025 | |
| 1.0.0-alpha.1 | 180 | 5/22/2025 |
See full release notes at: https://github.com/MichaelHochriegl/SignalRGen/releases/tag/v2.1.2