Md Mominul Islam | Software and Data Enginnering | SQL Server, .NET, Power BI, Azure Blog

while(!(succeed=try()));

LinkedIn Portfolio Banner

Latest

Home Top Ad

Responsive Ads Here

Post Top Ad

Responsive Ads Here

Wednesday, September 3, 2025

Building Real-Time Apps with SignalR in ASP.NET Core

 


Introduction to Real-Time Web Apps

Real-time web applications deliver instant updates, creating seamless, interactive user experiences. From live chat systems to dynamic dashboards displaying stock prices or server metrics, real-time functionality is essential in modern web development. This guide explores how to build such apps using ASP.NET Core SignalR, a powerful library for real-time communication. We’ll cover everything from basics to advanced scenarios, with practical examples, pros, cons, alternatives, and best practices.


Module 1: Understanding SignalR and Real-Time Communication

What Are Real-Time Web Apps?

Real-time apps allow servers to push updates to clients instantly, without repeated polling. Examples include:

  • Chat Apps: Instant messaging between users.
  • Dashboards: Live data visualizations for metrics like CPU usage or sales.
  • Collaborative Tools: Real-time document editing.
  • Notifications: Instant alerts for events like order updates.

What is SignalR?

ASP.NET Core SignalR simplifies real-time communication by providing a high-level API for bi-directional messaging. It primarily uses WebSockets but falls back to Server-Sent Events (SSE) or Long Polling for compatibility.

Key Features

  • Bi-Directional: Clients and servers can invoke methods on each other.
  • Transport Fallback: Automatically switches to SSE or Long Polling if WebSockets are unavailable.
  • Reconnection: Handles dropped connections gracefully.
  • Scalability: Supports backplanes like Redis or Azure SignalR Service.
  • Cross-Platform: Works with .NET, JavaScript, Java, and more.

Pros

  • Simplifies WebSocket complexity.
  • Broad browser compatibility.
  • Scalable with backplanes.
  • Integrates with ASP.NET Core ecosystem.
  • Supports multiple client platforms.

Cons

  • .NET-centric server-side.
  • Slight performance overhead vs. raw WebSockets.
  • Limited official SDKs for some languages.
  • No guaranteed message ordering.

Alternatives

  • Raw WebSockets: More control but complex to manage.
  • gRPC: High-performance RPC, no fallback mechanisms.
  • Socket.IO: Popular for Node.js environments.
  • Azure Web PubSub: Cloud-native real-time messaging.
  • Ably: Managed pub/sub platform.

Module 2: Building a Real-Time Chat App (Basic Example)

Let’s create a simple chat app to demonstrate SignalR’s core concepts.

Prerequisites

  • .NET SDK 6.0+
  • Visual Studio or VS Code
  • Basic C# and JavaScript knowledge
  • Node.js (for client-side dependencies)

Step 1: Create a Project

bash
dotnet new webapp -o SignalRChat
cd SignalRChat
dotnet add package Microsoft.AspNetCore.SignalR

Step 2: Create a SignalR Hub

Create Hubs/ChatHub.cs:

csharp
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}

Step 3: Configure SignalR

Update Program.cs:

csharp
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");
app.Run();

Step 4: Create the Client

Update Pages/Index.cshtml:

html
@page
@model IndexModel
@{
ViewData["Title"] = "Real-Time Chat";
}
<!DOCTYPE html>
<html>
<head>
<title>Real-Time Chat</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.min.js"></script>
</head>
<body>
<div>
<input type="text" id="userInput" placeholder="Your Name" />
<input type="text" id="messageInput" placeholder="Type a message..." />
<button onclick="sendMessage()">Send</button>
</div>
<ul id="messagesList"></ul>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.build();
connection.on("ReceiveMessage", (user, message) => {
const li = document.createElement("li");
li.textContent = `${user}: ${message}`;
document.getElementById("messagesList").appendChild(li);
});
connection.start().catch(err => console.error(err));
function sendMessage() {
const user = document.getElementById("userInput").value;
const message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message)
.catch(err => console.error(err));
}
</script>
</body>
</html>

Step 5: Run the App

bash
dotnet run

Open multiple browser tabs at https://localhost:5001, send messages, and see them appear instantly across tabs.


Module 3: Building a Real-Time Dashboard (Intermediate Example)

Now, let’s build a dashboard displaying live server metrics, using groups and periodic updates.

Step 1: Create a Dashboard Hub

Create Hubs/DashboardHub.cs:

csharp
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
public class DashboardHub : Hub
{
public async Task JoinDashboardGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("ReceiveUpdate", "System", $"Joined group: {groupName}");
}
public async Task SendMetricUpdate(string groupName, string metric, double value)
{
await Clients.Group(groupName).SendAsync("ReceiveMetric", metric, value);
}
}

Step 2: Simulate Metrics

Create Services/MetricService.cs:

csharp
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
public class MetricService : BackgroundService
{
private readonly IHubContext<DashboardHub> _hubContext;
public MetricService(IHubContext<DashboardHub> hubContext)
{
_hubContext = hubContext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var random = new Random();
while (!stoppingToken.IsCancellationRequested)
{
await _hubContext.Clients.Group("ServerMetrics")
.SendAsync("ReceiveMetric", "CPU Usage", random.Next(0, 100));
await _hubContext.Clients.Group("ServerMetrics")
.SendAsync("ReceiveMetric", "Memory Usage", random.Next(50, 200));
await Task.Delay(2000, stoppingToken);
}
}
}

Step 3: Update Program.cs

csharp
builder.Services.AddHostedService<MetricService>();
app.MapHub<DashboardHub>("/dashboardHub");

Step 4: Create the Dashboard Page

Create Pages/Dashboard.cshtml:

html
@page
@model DashboardModel
@{
ViewData["Title"] = "Real-Time Dashboard";
}
<!DOCTYPE html>
<html>
<head>
<title>Real-Time Dashboard</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<h1>Server Metrics Dashboard</h1>
<canvas id="metricsChart" width="400" height="200"></canvas>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/dashboardHub")
.build();
const ctx = document.getElementById("metricsChart").getContext("2d");
const chart = new Chart(ctx, {
type: "line",
data: {
labels: [],
datasets: [
{ label: "CPU Usage (%)", data: [], borderColor: "blue", fill: false },
{ label: "Memory Usage (MB)", data: [], borderColor: "green", fill: false }
]
},
options: { scales: { x: { title: { display: true, text: "Time" } }, y: { title: { display: true, text: "Value" } } } }
});
connection.on("ReceiveMetric", (metric, value) => {
const time = new Date().toLocaleTimeString();
chart.data.labels.push(time);
if (metric === "CPU Usage") {
chart.data.datasets[0].data.push(value);
} else if (metric === "Memory Usage") {
chart.data.datasets[1].data.push(value);
}
if (chart.data.labels.length > 20) {
chart.data.labels.shift();
chart.data.datasets.forEach(dataset => dataset.data.shift());
}
chart.update();
});
connection.start().then(() => {
connection.invoke("JoinDashboardGroup", "ServerMetrics")
.catch(err => console.error(err));
}).catch(err => console.error(err));
</script>
</body>
</html>

Step 5: Run and Test

Navigate to https://localhost:5001/Dashboard to see live CPU and memory usage updates.


Module 4: Advanced SignalR Features

Authentication

Secure hubs with JWT authentication. Update Program.cs:

csharp
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "your_issuer",
ValidAudience = "your_audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key_32_chars_long"))
};
});
app.UseAuthentication();

Add [Authorize] to ChatHub.cs:

csharp
[Authorize]
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}

Update the client with a JWT token:

html
<script>
const token = "your_jwt_token_here";
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub", { accessTokenFactory: () => token })
.build();
</script>

Scaling with Redis

Install the Redis package:

bash
dotnet add package Microsoft.AspNetCore.SignalR.StackExchangeRedis

Configure in Program.cs:

csharp
builder.Services.AddSignalR().AddStackExchangeRedis("localhost:6379");

Reconnection Handling

Add reconnection logic to the client:

html
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.withAutomaticReconnect([0, 2000, 10000, 30000])
.build();
connection.onreconnected(() => {
const li = document.createElement("li");
li.textContent = "Reconnected!";
document.getElementById("messagesList").appendChild(li);
});
</script>

Module 5: Best Practices and Standards

Best Practices

  • Secure Hubs: Use [Authorize] and validate inputs.
  • Version Hubs: Use names like ChatHubV2 for backward compatibility.
  • Optimize Payloads: Use MessagePack for large messages.
  • Use Groups: Target specific clients to reduce bandwidth.
  • Test Scalability: Use tools like k6 for load testing.
  • Monitor Logs: Track connection issues and errors.

Standards

  • WebSocket Protocol (RFC 6455): Ensure browser compatibility.
  • HTTP/2 WebSockets (RFC 8441): Use for performance.
  • CORS: Configure for cross-domain support.
  • MessagePack: Use for efficient serialization.

Module 6: Real-World Scenarios

Notifications

csharp
public class NotificationHub : Hub
{
public async Task SendNotification(string userId, string message)
{
await Clients.User(userId).SendAsync("ReceiveNotification", message);
}
}

Collaborative Editing

csharp
public class EditorHub : Hub
{
public async Task UpdateDocument(string documentId, string content)
{
await Clients.Group(documentId).SendAsync("ReceiveUpdate", content);
}
}

Module 7: Troubleshooting

  • WebSocket Failures: Check server and firewall settings.
  • CORS Issues: Configure CORS policies.
  • Message Loss: Use sequence numbers for ordering.
  • Debugging: Enable client logging with configureLogging(signalR.LogLevel.Information).

Conclusion

SignalR and ASP.NET Core make building real-time apps like chats and dashboards straightforward and scalable. This guide provides a foundation for beginners and advanced techniques for experienced developers.

No comments:

Post a Comment

Thanks for your valuable comment...........
Md. Mominul Islam

Post Bottom Ad

Responsive Ads Here