How to Implement SSE for Notification in .NET in Production Ready?

By ICSM Computer — Published: 15-Dec-2025 • Last updated: 16-Dec-2025 65

This is not a demo – this is the same pattern used in real chat/notification systems (WhatsApp-like browser notifications, dashboards, etc.).

1. When to Use SSE (Production Decision)

Use SSE when:

  • One-way server → browser notifications
  • Chat message alerts (not full chat transport)
  • Live counters, alerts, status updates
  • Browser-based apps (no mobile socket requirement)

Do NOT use SSE for:

  • Binary streaming
  • High-frequency bidirectional chat typing events
  • Mobile background connections (WebSocket better)

2. Architecture (Production)

Browser (EventSource)
        ↓
SSE Controller (long-lived HTTP)
        ↓
ConcurrentDictionary<UserId, SseClient>
        ↓
Notification Publisher (Chat / Job / API)

Key rules:

  • One SSE connection per user
  • No DB polling
  • No blocking threads
  • Heartbeat required
  • Auto-reconnect handled by browser

3. Core SSE Infrastructure (IMPORTANT)

3.1 SSE Client Model

public class SseClient
{
    public int UserId { get; set; }
    public HttpResponse Response { get; set; }
    public DateTime ConnectedAt { get; set; }
}

3.2 SSE Connection Manager (Thread-Safe)

public static class SseConnectionManager
{
    private static readonly ConcurrentDictionary<int, SseClient> _clients
        = new ConcurrentDictionary<int, SseClient>();

    public static bool AddClient(int userId, HttpResponse response)
    {
        RemoveClient(userId); // ensure single tab policy (optional)
        return _clients.TryAdd(userId, new SseClient
        {
            UserId = userId,
            Response = response,
            ConnectedAt = DateTime.UtcNow
        });
    }

    public static void RemoveClient(int userId)
    {
        _clients.TryRemove(userId, out _);
    }

    public static bool SendToUser(int userId, string eventName, string data)
    {
        if (!_clients.TryGetValue(userId, out var client))
            return false;

        try
        {
            client.Response.Write($"event: {eventName}\n");
            client.Response.Write($"data: {data}\n\n");
            client.Response.Flush();
            return true;
        }
        catch
        {
            RemoveClient(userId);
            return false;
        }
    }

    public static void Broadcast(string eventName, string data)
    {
        foreach (var userId in _clients.Keys)
        {
            SendToUser(userId, eventName, data);
        }
    }
}
  • Thread-safe
  • Memory-safe
  • Handles broken connections

4. SSE Controller (Production-Safe)

public class SseController : Controller
{
    [HttpGet]
    public void Subscribe()
    {
        int userId = GetLoggedInUserId();

        Response.ContentType = "text/event-stream";
        Response.CacheControl = "no-cache";
        Response.BufferOutput = false;

        SseConnectionManager.AddClient(userId, Response);

        try
        {
            while (Response.IsClientConnected)
            {
                // Heartbeat every 15 sec
                Response.Write("event: ping\n");
                Response.Write("data: {}\n\n");
                Response.Flush();

                Thread.Sleep(15000);
            }
        }
        finally
        {
            SseConnectionManager.RemoveClient(userId);
        }
    }

    private int GetLoggedInUserId()
    {
        return int.Parse(User.Identity.Name);
    }
}

Why heartbeat?

  • Prevents proxy/load-balancer timeout
  • Detects dead connections

5. Sending Notifications (Chat / API / Jobs)

Example: Chat Message Notification

public class NotificationService
{
    public static void NotifyUser(int userId, object payload)
    {
        string json = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
        SseConnectionManager.SendToUser(userId, "message", json);
    }
}

Usage:

NotificationService.NotifyUser(671496, new
{
    fromUserId = 655093,
    text = "Hello 👋",
    createdAt = DateTime.UtcNow
});

6. Browser Client (Production JS)

<script>
let source = new EventSource("/sse/subscribe");

source.addEventListener("message", function (e) {
    let data = JSON.parse(e.data);
    console.log("Message:", data);
});

source.addEventListener("ping", function () {
    console.log("heartbeat");
});

source.onerror = function () {
    console.log("SSE disconnected. Reconnecting...");
};
</script>
  • Auto-reconnect handled by browser
  • No polling
  • Low memory footprint

7. Multi-Tab Handling (WhatsApp-Like Behavior)

If same user opens multiple tabs, enforce single active tab:

public static bool AddClient(int userId, HttpResponse response)
{
    RemoveClient(userId); // kicks old tab
    return _clients.TryAdd(userId, new SseClient
    {
        UserId = userId,
        Response = response,
        ConnectedAt = DateTime.UtcNow
    });
}

Other tabs will automatically disconnect and can show popup:

“This account is active in another tab”

8. Production Hardening Checklist

IIS / Hosting

  • Disable response buffering
  • Increase request timeout
  • Enable keep-alive

Security

  • Auth-based user binding
  • No userId in query string
  • Rate-limit notification sends

Scalability

  • In-memory only → single server
  • Multi-server → use Redis Pub/Sub

9. Multi-Server (Redis) Architecture

Server A  ─┐
Server B  ─┼── Redis Pub/Sub ──► SSE Clients
Server C  ─┘

Each server:

  • Subscribes to Redis channel
  • Pushes events to connected users

10. Why Big Companies Use SSE

  • GitHub notifications
  • Slack web notifications
  • Trading dashboards
  • Monitoring systems

 

  • Lower cost than WebSockets
  • Works perfectly behind proxies
  • Auto-reconnect built-in

11. SSE vs WebSocket (Quick Decision)

Feature SSE WebSocket
Direction Server → Client Two-way
Complexity Low High
Scaling Easy Harder
Browser support Excellent Excellent
Chat notifications Best Overkill

Final Recommendation (For Your Chat App)

Use:

  • SSE → notifications, unread count, alerts
  • API → send message
  • Optional WebSocket → typing indicators only

 

Read Also 

ICSM Computer
ICSM Computer
IT-Hardware & Networking

Ravi Vishwakarma is a dedicated Software Developer with a passion for crafting efficient and innovative solutions. With a keen eye for detail and years of experience, he excels in developing robust software systems that meet client needs. His expertise spans across multiple programming languages and technologies, making him a valuable asset in any software development project.