End-to-end SignalR verification confirms that real-time events flow correctly across multiple browser clients. The scaling discussion is critical for production: in-memory SignalR (the default) works only for a single-server deployment. As soon as the application runs on multiple instances (Azure App Service scale-out, Kubernetes pods), clients connected to different instances cannot communicate. Azure SignalR Service solves this with a managed backplane — a single configuration change.
Multi-Tab Verification
-- ── End-to-end verification checklist ────────────────────────────────────
-- 1. OPEN TWO TABS viewing the same post (e.g., /posts/getting-started-dotnet)
-- Tab 1: Browser A (Chrome)
-- Tab 2: Browser B (Firefox, to ensure different sessions)
-- Both should show viewer count = 2 within 1-2 seconds of opening Tab 2
-- 2. INSPECT WebSocket connection (DevTools → Network → filter "WS")
-- Click the /hubs/blog entry
-- Tab "Messages" shows WebSocket frames:
-- ↑ (outgoing): JoinPostRoom invocation: {"type":1,"target":"JoinPostRoom","arguments":[42]}
-- ↓ (incoming): UpdateViewerCount: {"type":1,"target":"UpdateViewerCount","arguments":[42,2]}
-- 3. SUBMIT A COMMENT in Tab 1:
-- Tab 1: comment appears immediately (optimistic update)
-- Tab 2: comment appears within milliseconds (SignalR broadcast)
-- DevTools Tab 2: incoming ReceiveComment frame visible in WS messages
-- 4. VERIFY AUTH: open an Incognito tab (unauthenticated), navigate to the post
-- The CommentsComponent shows "Sign in to leave a comment" (no form)
-- In browser console: hub connection starts but JoinPostRoom succeeds ([AllowAnonymous])
-- Viewer count updates to 3 (incognito tab joined the room)
-- 5. SIMULATE DISCONNECT: Tab 2, DevTools → Network → "Offline"
-- Tab 2 connection state → Reconnecting (visible in debug overlay if implemented)
-- DevTools → Network → back "Online"
-- Tab 2 automatically reconnects within ~2 seconds
-- New comments submitted while Tab 2 was offline are NOT automatically replayed
-- (implement REST API refresh on reconnect to resync missed messages)
-- 6. VERIFY NOTIFICATION: log in as User A in Tab 1, as User B in Tab 2
-- User B views a post authored by User A
-- User B submits a comment
-- User A's notification bell badge increments without page reload
-- Click the bell: the new notification appears in the dropdown
Azure SignalR Service — Production Scaling
// ── Install: dotnet add package Microsoft.Azure.SignalR ────────────────────
// ── Program.cs — swap AddSignalR() with Azure SignalR ─────────────────────
if (builder.Environment.IsProduction())
{
// Azure SignalR Service replaces in-process WebSocket management
builder.Services.AddSignalR()
.AddAzureSignalR(builder.Configuration["Azure:SignalRConnectionString"]);
}
else
{
// Local development — in-memory (single process only)
builder.Services.AddSignalR(opts =>
opts.EnableDetailedErrors = true);
}
// ── That's it — no changes to hub code or Angular client ──────────────────
// Azure SignalR Service:
// - Routes all WebSocket connections through Azure's managed service
// - Each app instance communicates with the service, not directly with clients
// - Supports 100,000+ concurrent connections per unit (vs ~500 per server)
// - Scales horizontally without sticky sessions
// - Built-in retry and reconnection handling
// - Connection string from Azure Portal → SignalR Service → Keys
.AddAzureSignalR() is so valuable for production scale.onreconnected(), the Angular service fetches the latest data from the REST API. For comments, call commentsApi.getByPost(postId) and merge any new comments since the last known comment ID. For notifications, call notifApi.getUnread() to pick up any notifications delivered while disconnected. This ensures no data is lost between a disconnection and reconnection even if SignalR missed events cannot be replayed..AddAzureSignalR(opts => opts.ConnectionString = null, opts.Endpoints = [new ServiceEndpoint(new Uri(endpoint), new DefaultAzureCredential())]). This eliminates the shared key entirely, using the App Service’s Managed Identity for authentication to the SignalR Service.SignalR Transport Fallback Order
| Transport | Full-duplex | Performance | Fallback When |
|---|---|---|---|
| WebSockets | ✅ Yes | Excellent | Preferred — used by default |
| Server-Sent Events | ❌ Server→Client only | Good | WS blocked by proxy/firewall |
| Long Polling | ❌ Simulated | Poor | SSE not supported |
Common Mistakes
Mistake 1 — Deploying in-memory SignalR with multiple instances (messages lost)
❌ Wrong — 3 app instances; client on Instance A comments; only clients on Instance A receive the broadcast.
✅ Correct — Azure SignalR Service or Redis backplane for multi-instance; all clients receive all messages.
Mistake 2 — No resync on reconnect (missed messages during disconnect lost forever)
❌ Wrong — reconnection restores WebSocket but missed comments during disconnect never appear.
✅ Correct — on onreconnected(), fetch latest data from REST API to fill the gap.