GM.RealTime.Persistence
1.0.0
See the version list below for details.
dotnet add package GM.RealTime.Persistence --version 1.0.0
NuGet\Install-Package GM.RealTime.Persistence -Version 1.0.0
<PackageReference Include="GM.RealTime.Persistence" Version="1.0.0" />
<PackageVersion Include="GM.RealTime.Persistence" Version="1.0.0" />
<PackageReference Include="GM.RealTime.Persistence" />
paket add GM.RealTime.Persistence --version 1.0.0
#r "nuget: GM.RealTime.Persistence, 1.0.0"
#:package GM.RealTime.Persistence@1.0.0
#addin nuget:?package=GM.RealTime.Persistence&version=1.0.0
#tool nuget:?package=GM.RealTime.Persistence&version=1.0.0
<p align="center"> <img src="https://raw.githubusercontent.com/gmetskhvarishvili/GM.RealTime/master/icon.png" alt="GM.RealTime" width="140" height="140" /> </p>
GM.RealTime
SignalR-based real-time communication for the GM.* ecosystem. Push to users, connections, and
groups through a clean IRealTimeSender instead of wiring IHubContext<T> by hand; track presence
in a shared, lock-guarded connection registry (backed by
GM.Caching +
GM.DistributedLock, so it's Redis-ready across
nodes); and authenticate the WebSocket handshake with a JWT from the access_token query string
(pairs with GM.Identity). Targets .NET 10.
Packages
The three packages version and release together (lockstep):
| Package | What it gives you |
|---|---|
GM.RealTime |
IRealTimeSender, IRealTimeClient, INotificationHub, the NotificationHub, the JWT-from-query handshake, and AddGMRealTime() / MapGMRealTimeHub(). |
GM.RealTime.Domain |
Presence models (Connection, UserPresence, RealTimeMessage) and the IConnectionRegistry abstraction. No infrastructure dependencies. |
GM.RealTime.Persistence |
CacheConnectionRegistry — the registry over ICacheService, with every read-modify-write guarded by IDistributedLock. |
dotnet add package GM.RealTime
Quick start
using GM.RealTime;
// Presence is shared across nodes when the cache + lock are Redis-backed — register those first:
builder.Services.AddGMRedisCaching(o => o.ConnectionString = "localhost:6379");
builder.Services.AddGMRedisDistributedLock(o => o.ConnectionString = "localhost:6379");
builder.Services.AddGMRealTime(o => o.HubPath = "/hubs/realtime");
// JWT is validated by your existing setup (e.g. GM.Identity); GM.RealTime only teaches it to read
// the token from ?access_token=... on the WebSocket handshake.
builder.Services.AddAuthentication().AddJwtBearer(/* your issuer/audience/key */);
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGMRealTimeHub(); // maps NotificationHub at RealTimeOptions.HubPath
app.Run();
Without the Redis registrations, AddGMRealTime() falls back to the in-memory cache and lock
(single-process) — perfect for development.
Push messages
Inject IRealTimeSender anywhere — no IHubContext in your app code:
public class OrderNotifier(IRealTimeSender realtime)
{
public Task OrderShipped(string userId, Guid orderId) =>
realtime.SendToUserAsync(userId, "order.shipped", new { orderId });
}
SendToUserAsync looks the user's live connections up in the shared registry, so it targets every
device they have open — on any node. Also available: SendToConnectionAsync, SendToGroupAsync,
SendToAllAsync.
Presence
public class PresenceEndpoint(IConnectionRegistry registry)
{
public Task<bool> IsOnline(string userId) => registry.IsOnlineAsync(userId);
}
The NotificationHub records connects/disconnects automatically (keyed by the authenticated user),
and mirrors JoinGroup / LeaveGroup into both SignalR and the registry.
Client contract
The hub is strongly typed (Hub<IRealTimeClient>), so clients listen for one method:
connection.on("ReceiveMessage", m => console.log(m.event, m.payload));
How presence stays correct under load
Every connect/disconnect is a read-modify-write on a user's connection set. Under a burst of concurrent connections (multiple tabs, reconnects) those would race and lose ids. The registry takes a per-user distributed lock (GM.DistributedLock) around each mutation, and stores the set in GM.Caching — so it is both race-free and shared across every server instance.
Scale-out note: the registry (presence, targeting) is shared via Redis out of the box. To also fan out hub messages across multiple servers, add the standard SignalR Redis backplane (
Microsoft.AspNetCore.SignalR.StackExchangeRedis) alongside — the two are complementary.
Roadmap: a Herald (GM.Notifications) real-time channel
GM.RealTime is designed to drop into GM.Notifications as a new delivery channel next to Email / SMS / Push / Slack / WhatsApp. The sketch:
GM.Notifications.RealTime/
IRealTimeSenderService : (channel contract, like IEmailSenderService)
RealTimeSenderService : wraps GM.RealTime's IRealTimeSender, mapping a
RealTimeNotification -> realtime.SendToUserAsync(userId, "notification", dto)
AddRealTimeNotificationServices(this IServiceCollection) // mirrors AddEmailNotificationServices
- Add a
RealTimeNotification : NotificationBaseentity (channel = RealTime) inGM.Notifications.Domain, with a matching EF configuration inGM.Notifications.Persistence. - A
RealTimeWorker(like the Email/SMS workers) polls pendingRealTimeNotifications and callsIRealTimeSenderService.SendAsync, which delegates toIRealTimeSender.SendToUserAsync— marking the notificationSent, orFailed(with retry) if the user is offline, exactly like the other channels. Presence (IConnectionRegistry.IsOnlineAsync) lets the worker skip or defer delivery to offline users.
That layering keeps GM.RealTime standalone while making it a first-class Herald channel.
Repository layout
GM.RealTime/ # IRealTimeSender, hub, JWT handshake, AddGMRealTime / MapGMRealTimeHub
GM.RealTime.Domain/ # Connection, UserPresence, RealTimeMessage, IConnectionRegistry
GM.RealTime.Persistence/ # CacheConnectionRegistry (GM.Caching + GM.DistributedLock)
tests/GM.RealTime.Tests/ # xUnit tests for the connection registry
Building & testing
dotnet build -c Release
dotnet test -c Release
Releasing
Versioning is automated from Conventional Commits —
see CONTRIBUTING.md. All three packages share one version
(Directory.Build.props) and publish together to nuget.org on each release.
License
MIT — see LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- GM.Caching (>= 1.0.0)
- GM.DistributedLock (>= 1.0.0)
- GM.RealTime.Domain (>= 1.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Options (>= 10.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on GM.RealTime.Persistence:
| Package | Downloads |
|---|---|
|
GM.RealTime
SignalR-based real-time communication for the GM.* ecosystem. IRealTimeSender pushes to users/connections/groups without touching IHubContext directly; a presence-tracking hub registers connections in a shared, lock-guarded registry (GM.Caching + GM.DistributedLock); and the WebSocket handshake reads the JWT from the access_token query string (pairs with GM.Identity). One call: AddGMRealTime(). |
GitHub repositories
This package is not used by any popular GitHub repositories.