---
title: "How to Implement SSE for Notification in .NET in Production Ready?"  
description: "Below is a production-ready, battle-tested way to implement Server-Sent Events (SSE) for notifications in .NET, specifically ASP.NET MVC 5 / .NET Framework 4.8,"  
author: "Ravi Vishwakarma"  
published: 2025-12-15  
updated: 2025-12-16  
canonical: https://answers.mindstick.com/blog/7/how-to-implement-sse-for-notification-in-dot-net-in-production-ready  
category: "technology"  
tags: ["tech", ".net programming", "asp.net"]  
reading_time: 4 minutes  

---

# How to Implement SSE for Notification in .NET in Production Ready?

This is **not a demo** – this is the **same pattern used in real chat/[notification](https://www.mindstick.com/blog/205/property-notification-in-c-sharp) systems** (WhatsApp-like browser notifications, dashboards, etc.).

## 1. When to Use SSE (Production Decision)

[Use **SSE**](https://www.mindstick.com/interview/34423/what-is-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](https://www.mindstick.com/articles/65/how-to-change-background-color-of-tab-control-in-c-sharp) connections (WebSocket better)

## 2. Architecture (Production)

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

Key rules:

- **One** [**SSE connection**](https://answers.mindstick.com/qa/116201/how-to-implement-the-sse-in-dot-net) **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

```cs
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)

```cs
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)

```cs
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

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

## Usage:

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

## 6. Browser Client (Production JS)

```html
<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](https://answers.mindstick.com/qa/95656/how-do-i-stop-firefox-from-opening-multiple-tabs)**, enforce **single active tab**:

```cs
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](https://answers.mindstick.com/qa/115804/why-can-t-i-post-answers-even-though-my-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](https://www.mindstick.com/articles/512/query-string-in-asp-dot-net)
- Rate-limit notification sends

### Scalability

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

## 9. Multi-Server (Redis) Architecture

```plaintext
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](https://www.mindstick.com/articles/337717/understanding-websockets-and-their-role-in-real-time-web-applications)
> - 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

- [**What is SSE?**](https://www.mindstick.com/interview/34423/what-is-sse)
- [**How to implement Websocket AP?**](https://www.mindstick.com/interview/34422/how-to-implement-websocket-ap)

---

Original Source: https://answers.mindstick.com/blog/7/how-to-implement-sse-for-notification-in-dot-net-in-production-ready

Copyright © MindStick Software Pvt. Ltd. This Markdown version is provided for developers, AI systems, and offline reading.
