<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ravi Vishwakarma</title>
    <description>The latest articles on DEV Community by Ravi Vishwakarma (@ravi-vishwakarma-hash).</description>
    <link>https://dev.to/ravi-vishwakarma-hash</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F927623%2Fc560ed5b-5722-4b25-b9f7-36205b08c154.jpeg</url>
      <title>DEV Community: Ravi Vishwakarma</title>
      <link>https://dev.to/ravi-vishwakarma-hash</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ravi-vishwakarma-hash"/>
    <language>en</language>
    <item>
      <title>Why HTTP Needed the QUERY Method</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Sat, 11 Jul 2026 09:17:42 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/why-http-needed-the-query-method-3p1h</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/why-http-needed-the-query-method-3p1h</guid>
      <description>&lt;h1&gt;
  
  
  HTTP Finally Gets a New Request Method: Meet &lt;code&gt;QUERY&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;For years, HTTP developers have worked with a familiar set of request methods—&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt;. These methods have served the web remarkably well, but one common problem has always been awkward to solve:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you send a complex query to the server without pretending it's a state-changing operation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer was usually one of two compromises:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stuff everything into a long &lt;code&gt;GET&lt;/code&gt; URL.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;POST&lt;/code&gt; even though you're only reading data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, HTTP has a better answer.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;RFC 10008&lt;/strong&gt;, the HTTP ecosystem introduces a new standardized request method: &lt;strong&gt;&lt;code&gt;QUERY&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with GET
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GET&lt;/code&gt; is the natural choice for retrieving data because it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Safe (doesn't modify server state)&lt;/li&gt;
&lt;li&gt;Idempotent&lt;/li&gt;
&lt;li&gt;Cache-friendly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, &lt;code&gt;GET&lt;/code&gt; requests carry parameters in the URL.&lt;/p&gt;

&lt;p&gt;Imagine searching an e-commerce website with dozens of filters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /products?category=laptops&amp;amp;brand=Dell&amp;amp;brand=Lenovo&amp;amp;priceMin=500&amp;amp;priceMax=1500&amp;amp;sort=-rating&amp;amp;page=3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As filters become more complex, URLs become:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Difficult to read&lt;/li&gt;
&lt;li&gt;Hard to maintain&lt;/li&gt;
&lt;li&gt;Limited by URL length restrictions in some environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even worse, expressing deeply nested filters in a query string can become nearly impossible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Developers Used POST
&lt;/h2&gt;

&lt;p&gt;To avoid enormous URLs, many APIs switched to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /products/search
Content-Type: application/json

{
  "category": "laptops",
  "brands": ["Dell", "Lenovo"],
  "price": {
    "min": 500,
    "max": 1500
  },
  "sort": "-rating"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works technically.&lt;/p&gt;

&lt;p&gt;But semantically, it's misleading.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;POST&lt;/code&gt; is intended for operations that may change server state, while a search is simply retrieving information.&lt;/p&gt;

&lt;p&gt;This mismatch affects caching, retries, API documentation, and HTTP semantics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter &lt;code&gt;QUERY&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The new &lt;code&gt;QUERY&lt;/code&gt; method is designed specifically for read-only operations that require a request body.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;QUERY /products
Content-Type: application/json

{
  "category": "laptops",
  "brands": ["Dell", "Lenovo"],
  "price": {
    "min": 500,
    "max": 1500
  },
  "sort": "-rating"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the intent is clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client is retrieving data.&lt;/li&gt;
&lt;li&gt;The request can include a rich JSON body.&lt;/li&gt;
&lt;li&gt;The server promises not to modify server state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Characteristics
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;QUERY&lt;/code&gt; is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Safe&lt;/li&gt;
&lt;li&gt;Idempotent&lt;/li&gt;
&lt;li&gt;Designed to support a request body&lt;/li&gt;
&lt;li&gt;Intended for complex retrieval operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In many situations, it becomes the ideal middle ground between &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  GET vs POST vs QUERY
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;GET&lt;/th&gt;
&lt;th&gt;POST&lt;/th&gt;
&lt;th&gt;QUERY&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read data&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request body&lt;/td&gt;
&lt;td&gt;No (not generally used)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safe&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotent&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex filters&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correct HTTP semantics&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (for reads)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl357bzbb94o6k1ahs2cr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl357bzbb94o6k1ahs2cr.jpg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;QUERY&lt;/code&gt; is especially useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced product searches&lt;/li&gt;
&lt;li&gt;Analytics dashboards&lt;/li&gt;
&lt;li&gt;Business intelligence queries&lt;/li&gt;
&lt;li&gt;Graph-style filtering&lt;/li&gt;
&lt;li&gt;GIS and map searches&lt;/li&gt;
&lt;li&gt;Large reporting APIs&lt;/li&gt;
&lt;li&gt;Log search platforms&lt;/li&gt;
&lt;li&gt;AI and vector database queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anywhere a client needs to send a structured query without changing data, &lt;code&gt;QUERY&lt;/code&gt; is a natural fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Will It Replace POST?
&lt;/h2&gt;

&lt;p&gt;Not immediately.&lt;/p&gt;

&lt;p&gt;The web ecosystem is vast. Browsers, HTTP libraries, proxies, API gateways, CDNs, and frameworks all need to support the new method. Existing APIs that already use &lt;code&gt;POST /search&lt;/code&gt; will likely continue to do so until support for &lt;code&gt;QUERY&lt;/code&gt; becomes widespread.&lt;/p&gt;

&lt;p&gt;Over time, however, new APIs may adopt &lt;code&gt;QUERY&lt;/code&gt; to better express read-only operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for API Designers
&lt;/h2&gt;

&lt;p&gt;For years, developers had to choose between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Correct semantics (&lt;code&gt;GET&lt;/code&gt;) with awkward URLs&lt;/li&gt;
&lt;li&gt;Convenient request bodies (&lt;code&gt;POST&lt;/code&gt;) with incorrect semantics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;QUERY&lt;/code&gt; removes that trade-off.&lt;/p&gt;

&lt;p&gt;When an endpoint retrieves data but requires a structured request body, developers no longer have to misuse &lt;code&gt;POST&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;New HTTP methods are introduced very rarely.&lt;/p&gt;

&lt;p&gt;The introduction of &lt;code&gt;PATCH&lt;/code&gt; was a significant milestone, and now &lt;code&gt;QUERY&lt;/code&gt; represents another meaningful evolution of the HTTP protocol.&lt;/p&gt;

&lt;p&gt;It is a small addition with a significant impact, making HTTP more expressive while preserving the principles that have made it successful for decades.&lt;/p&gt;

&lt;p&gt;As support spreads across frameworks, servers, proxies, and client libraries, &lt;code&gt;QUERY&lt;/code&gt; has the potential to become the standard choice for complex, read-only operations.&lt;/p&gt;

&lt;p&gt;HTTP doesn't evolve often, but when it does, it's worth paying attention.&lt;/p&gt;

</description>
      <category>http</category>
      <category>development</category>
      <category>rfc</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a Live Data Dashboard with Angular and ASP.NET Core: A Complete Step-by-Step Guide (Beginner to Advanced)</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Tue, 30 Jun 2026 06:39:12 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/building-a-live-data-dashboard-with-angular-and-aspnet-core-a-complete-step-by-step-guide-35l0</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/building-a-live-data-dashboard-with-angular-and-aspnet-core-a-complete-step-by-step-guide-35l0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Learn how to build a real-time, live-updating data dashboard using Angular on the frontend and ASP.NET Core with SignalR on the backend. This guide walks through every step — from project setup to production deployment — with full code examples, best practices, and SEO-friendly structure for your own blog or documentation.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;What You Will Build&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Understanding the Architecture&lt;/li&gt;
&lt;li&gt;Part 1 (Beginner): Setting Up the ASP.NET Core Backend&lt;/li&gt;
&lt;li&gt;Part 2 (Beginner): Adding SignalR for Real-Time Communication&lt;/li&gt;
&lt;li&gt;Part 3 (Beginner): Setting Up the Angular Frontend&lt;/li&gt;
&lt;li&gt;Part 4 (Intermediate): Connecting Angular to SignalR&lt;/li&gt;
&lt;li&gt;Part 5 (Intermediate): Building the Dashboard UI with Live Charts&lt;/li&gt;
&lt;li&gt;Part 6 (Intermediate): Styling the Dashboard&lt;/li&gt;
&lt;li&gt;Part 7 (Advanced): Handling Reconnection and Error States&lt;/li&gt;
&lt;li&gt;Part 8 (Advanced): Adding Authentication with JWT&lt;/li&gt;
&lt;li&gt;Part 9 (Advanced): Scaling SignalR with Redis Backplane&lt;/li&gt;
&lt;li&gt;Part 10 (Advanced): Performance Optimization&lt;/li&gt;
&lt;li&gt;Part 11 (Advanced): Testing the Application&lt;/li&gt;
&lt;li&gt;Part 12 (Advanced): Deploying to Production&lt;/li&gt;
&lt;li&gt;Common Errors and Troubleshooting&lt;/li&gt;
&lt;li&gt;Best Practices Checklist&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions (FAQ)&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;Real-time dashboards have become a core requirement in modern web applications — whether you're tracking stock prices, monitoring IoT sensor data, displaying server health metrics, or showing live sales numbers. Instead of forcing users to refresh a page to see updated data, a &lt;strong&gt;live data dashboard&lt;/strong&gt; pushes new information to the browser the moment it becomes available.&lt;/p&gt;

&lt;p&gt;In this guide, you will learn how to build a complete live data dashboard using two of the most popular technologies in the enterprise web development world: &lt;strong&gt;Angular&lt;/strong&gt; for the frontend and &lt;strong&gt;ASP.NET Core&lt;/strong&gt; for the backend, connected through &lt;strong&gt;SignalR&lt;/strong&gt;, Microsoft's real-time communication library.&lt;/p&gt;

&lt;p&gt;This tutorial is structured to take you from absolute beginner concepts all the way to advanced, production-ready practices including authentication, scaling, and deployment. Whether you are a student building your first real-time app or a professional developer architecting a production dashboard, this guide has something for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. What You Will Build
&lt;/h2&gt;

&lt;p&gt;By the end of this tutorial, you will have a fully functional application consisting of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;ASP.NET Core Web API&lt;/strong&gt; backend that generates and broadcasts live data (for example, simulated stock prices, server metrics, or sensor readings).&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;SignalR Hub&lt;/strong&gt; that pushes updates to connected clients instantly, without polling.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Angular application&lt;/strong&gt; that connects to the SignalR hub and updates its UI in real time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live charts&lt;/strong&gt; built with a charting library (Chart.js or ngx-charts) that animate as new data arrives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT authentication&lt;/strong&gt; to secure the dashboard so only logged-in users can view live data.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;production deployment&lt;/strong&gt; setup using Docker and IIS/Azure.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, make sure you have the following installed and a basic understanding of these technologies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Software Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; (LTS version) and npm&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Angular CLI&lt;/strong&gt; (&lt;code&gt;npm install -g @angular/cli&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.NET SDK&lt;/strong&gt; (latest LTS version of .NET)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual Studio Code&lt;/strong&gt; or &lt;strong&gt;Visual Studio 2022&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git&lt;/strong&gt; for version control&lt;/li&gt;
&lt;li&gt;(Optional, for advanced sections) &lt;strong&gt;Docker Desktop&lt;/strong&gt; and a &lt;strong&gt;Redis&lt;/strong&gt; instance&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Knowledge Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of TypeScript and Angular components/services&lt;/li&gt;
&lt;li&gt;Basic understanding of C# and ASP.NET Core controllers&lt;/li&gt;
&lt;li&gt;Familiarity with REST APIs (helpful but not mandatory)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are completely new to Angular or ASP.NET Core, don't worry — each step includes explanations of &lt;em&gt;why&lt;/em&gt; we are doing something, not just &lt;em&gt;how&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Understanding the Architecture
&lt;/h2&gt;

&lt;p&gt;Before writing any code, it's important to understand how the pieces fit together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ┌─────────────────────┐         WebSocket / SignalR        ┌──────────────────────┐
 │                      │ &amp;lt;---------------------------------&amp;gt; │                      │
 │   Angular Frontend   │                                     │  ASP.NET Core Backend│
 │  (Dashboard UI +     │         REST API (initial data)     │  (Web API + SignalR  │
 │   SignalR Client)    │ &amp;lt;---------------------------------- │   Hub + Data Service)│
 │                      │                                     │                      │
 └─────────────────────┘                                     └──────────────────────┘
                                                                         │
                                                                         ▼
                                                              ┌──────────────────────┐
                                                              │  Data Source (DB,    │
                                                              │  IoT feed, simulated │
                                                              │  data generator, etc)│
                                                              └──────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key concept:&lt;/strong&gt; Instead of the Angular app repeatedly calling an API (polling) every few seconds to check for new data, the ASP.NET Core backend uses &lt;strong&gt;SignalR&lt;/strong&gt; to push data directly to connected clients over a persistent WebSocket connection. This is far more efficient and provides true real-time updates.&lt;/p&gt;

&lt;p&gt;We will use a &lt;strong&gt;REST API call&lt;/strong&gt; for the initial page load (to get historical/starting data) and &lt;strong&gt;SignalR&lt;/strong&gt; for all subsequent live updates.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Part 1 (Beginner): Setting Up the ASP.NET Core Backend
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1.1: Create the Web API Project
&lt;/h3&gt;

&lt;p&gt;Open your terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new webapi &lt;span class="nt"&gt;-n&lt;/span&gt; LiveDashboard.Api
&lt;span class="nb"&gt;cd &lt;/span&gt;LiveDashboard.Api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new ASP.NET Core Web API project. If you're prompted to choose between minimal APIs and controllers, this guide uses &lt;strong&gt;controllers&lt;/strong&gt; for clarity, but the concepts apply equally to minimal API style.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1.2: Project Structure
&lt;/h3&gt;

&lt;p&gt;Organize your project with the following folders for clarity as the app grows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LiveDashboard.Api/
 ├── Controllers/
 ├── Hubs/
 ├── Models/
 ├── Services/
 ├── Program.cs
 └── appsettings.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create these folders inside your project directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1.3: Define the Data Model
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;Models/MetricData.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Models&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MetricData&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MetricName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;Timestamp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple model represents a single data point — for example, a CPU usage percentage, a stock price, or a temperature reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1.4: Create a Simulated Data Service
&lt;/h3&gt;

&lt;p&gt;In a real application, this data might come from a database, a message queue, or an external API. For this tutorial, we'll simulate live data so you can see the dashboard working immediately.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;Services/DataGeneratorService.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Services&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataGeneratorService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt; &lt;span class="n"&gt;_random&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;MetricData&lt;/span&gt; &lt;span class="nf"&gt;GenerateMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;baseValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NextDouble&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MetricData&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;MetricName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseValue&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;Timestamp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register this service in &lt;code&gt;Program.cs&lt;/code&gt; (we'll come back to &lt;code&gt;Program.cs&lt;/code&gt; again shortly):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DataGeneratorService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1.5: Create a Basic REST Endpoint
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;Controllers/MetricsController.cs&lt;/code&gt; to allow the Angular app to fetch the initial set of data when it first loads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Controllers&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MetricsController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;DataGeneratorService&lt;/span&gt; &lt;span class="n"&gt;_generator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MetricsController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DataGeneratorService&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_generator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"snapshot"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;GetSnapshot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;_generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CPU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;_generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Memory"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;_generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RequestsPerSecond"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1.6: Enable CORS
&lt;/h3&gt;

&lt;p&gt;Since Angular (running on port 4200 in development) and ASP.NET Core (running on a different port) are different origins, you need to enable CORS. Add this to &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AllowAngularDev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithOrigins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:4200"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AllowAnyHeader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AllowAnyMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AllowCredentials&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And later, in the middleware pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseCors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AllowAngularDev"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;AllowCredentials()&lt;/code&gt; is required for SignalR connections to work correctly with CORS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1.7: Run and Verify
&lt;/h3&gt;

&lt;p&gt;Run the backend with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;code&gt;https://localhost:&amp;lt;port&amp;gt;/api/metrics/snapshot&lt;/code&gt; in your browser or via a tool like Postman to confirm you receive a JSON array of metric data.&lt;/p&gt;

&lt;p&gt;At this point, you have a working REST API. Next, we'll add real-time capability using SignalR.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Part 2 (Beginner): Adding SignalR for Real-Time Communication
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 2.1: What Is SignalR?
&lt;/h3&gt;

&lt;p&gt;SignalR is a library built into ASP.NET Core that simplifies adding real-time web functionality. It abstracts away the complexity of WebSockets, automatically falling back to other transport methods (like Server-Sent Events or long polling) when WebSockets aren't available, and handles connection management for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2.2: Create the Hub
&lt;/h3&gt;

&lt;p&gt;A "Hub" in SignalR is the core component that handles communication between server and clients. Create &lt;code&gt;Hubs/DashboardHub.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.SignalR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Hubs&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardHub&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hub&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnConnectedAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Caller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ReceiveMessage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Connected to live dashboard hub."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnConnectedAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This hub doesn't need many methods yet — most of the data pushing will happen from a background service, not from client-invoked methods. The hub mainly serves as the "pipe" through which data flows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2.3: Register SignalR in Program.cs
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Hubs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSignalR&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DataGeneratorService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AllowAngularDev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithOrigins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:4200"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AllowAnyHeader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AllowAnyMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
              &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AllowCredentials&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseCors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AllowAngularDev"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRouting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapHub&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DashboardHub&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hubs/dashboard"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key line here is &lt;code&gt;app.MapHub&amp;lt;DashboardHub&amp;gt;("/hubs/dashboard")&lt;/code&gt;, which exposes the hub at a specific URL that the Angular client will connect to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2.4: Create a Background Service to Push Live Data
&lt;/h3&gt;

&lt;p&gt;This is where the "live" part of the dashboard comes to life. We'll create a hosted background service that runs continuously, generates new data every few seconds, and broadcasts it to all connected clients via the hub.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;Services/MetricBroadcastService.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.SignalR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Hubs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;LiveDashboard.Api.Services&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MetricBroadcastService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BackgroundService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IHubContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DashboardHub&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_hubContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;DataGeneratorService&lt;/span&gt; &lt;span class="n"&gt;_generator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MetricBroadcastService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHubContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DashboardHub&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hubContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DataGeneratorService&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_hubContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hubContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;_generator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cpu&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CPU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Memory"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rps&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RequestsPerSecond"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_hubContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ReceiveMetric"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_hubContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ReceiveMetric"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_hubContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ReceiveMetric"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// push every 2 seconds&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register the background service in &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHostedService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MetricBroadcastService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How this works:&lt;/strong&gt; &lt;code&gt;IHubContext&amp;lt;DashboardHub&amp;gt;&lt;/code&gt; lets you send messages to clients from anywhere in your application (not just from within the hub itself) — perfect for a background service that has no direct connection to a specific client, but needs to broadcast to all of them. &lt;code&gt;Clients.All.SendAsync("ReceiveMetric", ...)&lt;/code&gt; sends a message named &lt;code&gt;"ReceiveMetric"&lt;/code&gt; with the metric data as a payload to every connected client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2.5: Verify with a Test Client (Optional)
&lt;/h3&gt;

&lt;p&gt;Before building the Angular frontend, you can test the SignalR hub using a tool like the &lt;a href="https://learning.postman.com/docs/sending-requests/signalr/signalr/" rel="noopener noreferrer"&gt;SignalR test client in Postman&lt;/a&gt; or a simple HTML page with the SignalR JavaScript client. This isn't required, but it can help isolate backend issues from frontend issues if something doesn't work later.&lt;/p&gt;

&lt;p&gt;At this point, your backend is complete for the beginner phase: it has a REST endpoint for the initial snapshot and a SignalR hub broadcasting new data every two seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Part 3 (Beginner): Setting Up the Angular Frontend
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 3.1: Create the Angular Project
&lt;/h3&gt;

&lt;p&gt;In a separate folder (sibling to your backend project), run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new live-dashboard-client &lt;span class="nt"&gt;--routing&lt;/span&gt; &lt;span class="nt"&gt;--style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;scss
&lt;span class="nb"&gt;cd &lt;/span&gt;live-dashboard-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose the default options when prompted, or customize as you prefer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3.2: Install the SignalR Client Library
&lt;/h3&gt;

&lt;p&gt;Angular communicates with the SignalR hub using Microsoft's official JavaScript/TypeScript client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @microsoft/signalr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3.3: Install a Charting Library
&lt;/h3&gt;

&lt;p&gt;For this tutorial we'll use &lt;strong&gt;Chart.js&lt;/strong&gt; with the &lt;code&gt;ng2-charts&lt;/code&gt; Angular wrapper, since it's lightweight and beginner-friendly. (Advanced alternative: &lt;code&gt;ngx-charts&lt;/code&gt;, covered briefly later.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;chart.js ng2-charts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3.4: Project Structure
&lt;/h3&gt;

&lt;p&gt;Generate the core building blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate service services/signalr
ng generate service services/metrics
ng generate component components/dashboard
ng generate component components/metric-card
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a clean separation: a service to manage the SignalR connection, a service to manage metric state, and components for the visual layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3.5: Define the Metric Model
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;src/app/models/metric-data.model.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MetricData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mirrors the C# &lt;code&gt;MetricData&lt;/code&gt; class on the backend, ensuring type safety across the stack.&lt;/p&gt;

&lt;p&gt;At this point, your Angular project is scaffolded and ready to be wired up to the live backend. In the next section, we'll connect it to SignalR.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Part 4 (Intermediate): Connecting Angular to SignalR
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 4.1: Build the SignalR Service
&lt;/h3&gt;

&lt;p&gt;This service will manage the connection lifecycle (connect, disconnect, reconnect) and expose an Observable stream of incoming metric data that any component can subscribe to.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;src/app/services/signalr.service.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;signalR&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@microsoft/signalr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetricData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/metric-data.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../environments/environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SignalrService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;hubConnection&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signalR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HubConnection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;metricSubject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Subject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MetricData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;metric$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricSubject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asObservable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;startConnection&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;signalR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HubConnectionBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiBaseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/hubs/dashboard`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withAutomaticReconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signalR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Information&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SignalR connection started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error starting SignalR connection:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;registerHandlers&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ReceiveMetric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MetricData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricSubject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ReceiveMessage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server message:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;stopConnection&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key concepts explained:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HubConnectionBuilder().withUrl(...)&lt;/code&gt; configures the connection to point at the hub endpoint we exposed on the backend (&lt;code&gt;/hubs/dashboard&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.withAutomaticReconnect()&lt;/code&gt; tells SignalR to automatically attempt reconnection if the connection drops — essential for a reliable live dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.hubConnection.on('ReceiveMetric', ...)&lt;/code&gt; registers a handler for the exact same message name (&lt;code&gt;"ReceiveMetric"&lt;/code&gt;) that the backend uses in &lt;code&gt;SendAsync("ReceiveMetric", ...)&lt;/code&gt;. These names &lt;strong&gt;must match exactly&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;We use an RxJS &lt;code&gt;Subject&lt;/code&gt; to convert SignalR's callback-based API into an Observable stream, which fits naturally into Angular's reactive patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4.2: Create the Environment Configuration
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;src/environments/environment.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiBaseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://localhost:7000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// match your backend's actual port&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;src/environments/environment.prod.ts&lt;/code&gt; for production builds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiBaseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://your-production-api.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4.3: Build the Metrics Service
&lt;/h3&gt;

&lt;p&gt;This service will fetch the initial snapshot via REST and maintain the current state of each metric as live updates arrive.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;src/app/services/metrics.service.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetricData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/metric-data.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../environments/environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SignalrService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./signalr.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MetricsService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;metricsMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MetricData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;metricsSubject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MetricData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;metrics$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricsSubject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asObservable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;signalr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SignalrService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signalr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metric$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upsertMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;loadInitialSnapshot&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MetricData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiBaseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/metrics/snapshot`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialData&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;initialData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricsMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emitMetrics&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;upsertMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MetricData&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricsMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emitMetrics&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;emitMetrics&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricsSubject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricsMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why a &lt;code&gt;Map&lt;/code&gt; keyed by metric name?&lt;/strong&gt; Each new live update replaces the latest value for that specific metric (e.g., CPU), rather than appending to an ever-growing list. This keeps the current-state view of the dashboard accurate and memory-efficient. (We'll handle historical trend data separately for charts in the next section.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4.4: Enable HttpClient
&lt;/h3&gt;

&lt;p&gt;Make sure &lt;code&gt;HttpClient&lt;/code&gt; is available. In a standalone Angular app (&lt;code&gt;app.config.ts&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideHttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;provideHttpClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using an NgModule-based app instead, import &lt;code&gt;HttpClientModule&lt;/code&gt; into your &lt;code&gt;AppModule&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4.5: Initialize the Connection in the Dashboard Component
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;src/app/components/dashboard/dashboard.component.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnDestroy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommonModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetricsService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../services/metrics.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SignalrService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../services/signalr.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetricData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../models/metric-data.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CommonModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dashboard.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dashboard.component.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnDestroy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MetricData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;metricsService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MetricsService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;signalrService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SignalrService&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signalrService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricsService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadInitialSnapshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricsService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metrics$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnDestroy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signalrService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this stage, your Angular app connects to the backend, fetches an initial snapshot, and listens for live updates. Next, we'll render this data visually.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Part 5 (Intermediate): Building the Dashboard UI with Live Charts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 5.1: Basic Metric Cards (Numbers View)
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;dashboard.component.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dashboard"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Live Data Dashboard&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"metric-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;app-metric-card&lt;/span&gt;
      &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let metric of metrics"&lt;/span&gt;
      &lt;span class="na"&gt;[name]=&lt;/span&gt;&lt;span class="s"&gt;"metric.metricName"&lt;/span&gt;
      &lt;span class="na"&gt;[value]=&lt;/span&gt;&lt;span class="s"&gt;"metric.value"&lt;/span&gt;
      &lt;span class="na"&gt;[timestamp]=&lt;/span&gt;&lt;span class="s"&gt;"metric.timestamp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/app-metric-card&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;metric-card.component.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommonModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-metric-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CommonModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./metric-card.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./metric-card.component.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MetricCardComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;metric-card.component.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"metric-card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;{{ name }}&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ value }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;Updated: {{ timestamp | date:'mediumTime' }}&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a simple, working live-updating numeric dashboard. Now let's add line charts to visualize trends over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5.2: Setting Up ng2-charts
&lt;/h3&gt;

&lt;p&gt;In your standalone component or module, import the chart provider. With standalone components, register it in &lt;code&gt;app.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideCharts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withDefaultRegisterables&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ng2-charts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;provideHttpClient&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;provideCharts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withDefaultRegisterables&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5.3: Create a Live Chart Component
&lt;/h3&gt;

&lt;p&gt;Generate a new component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate component components/live-chart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This component needs to maintain a rolling history of values for a given metric (e.g., the last 30 data points) so the chart shows a trend rather than just the latest number.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;live-chart.component.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnChanges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SimpleChanges&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommonModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BaseChartDirective&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ng2-charts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChartConfiguration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ChartData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chart.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-live-chart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CommonModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BaseChartDirective&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./live-chart.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./live-chart.component.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LiveChartComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnChanges&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;maxPoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;lineChartData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChartData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;datasets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;borderColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#3f8cff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rgba(63, 140, 255, 0.15)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;pointRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;lineChartOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChartConfiguration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;responsive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;scales&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;beginAtZero&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnChanges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SimpleChanges&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;firstChange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addDataPoint&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;firstChange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addDataPoint&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;addDataPoint&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineChartData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineChartData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;datasets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLocaleTimeString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxPoints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Reassign to trigger change detection in ng2-charts&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineChartData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineChartData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;datasets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineChartData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;datasets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;live-chart.component.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h4&amp;gt;&lt;/span&gt;{{ label }} Trend&lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt;
    &lt;span class="na"&gt;baseChart&lt;/span&gt;
    &lt;span class="na"&gt;[data]=&lt;/span&gt;&lt;span class="s"&gt;"lineChartData"&lt;/span&gt;
    &lt;span class="na"&gt;[options]=&lt;/span&gt;&lt;span class="s"&gt;"lineChartOptions"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"line"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5.4: Wire the Chart into the Dashboard
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;dashboard.component.html&lt;/code&gt; to include the chart alongside the cards:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dashboard"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Live Data Dashboard&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"metric-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;app-metric-card&lt;/span&gt;
      &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let metric of metrics"&lt;/span&gt;
      &lt;span class="na"&gt;[name]=&lt;/span&gt;&lt;span class="s"&gt;"metric.metricName"&lt;/span&gt;
      &lt;span class="na"&gt;[value]=&lt;/span&gt;&lt;span class="s"&gt;"metric.value"&lt;/span&gt;
      &lt;span class="na"&gt;[timestamp]=&lt;/span&gt;&lt;span class="s"&gt;"metric.timestamp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/app-metric-card&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;app-live-chart&lt;/span&gt;
      &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let metric of metrics"&lt;/span&gt;
      &lt;span class="na"&gt;[label]=&lt;/span&gt;&lt;span class="s"&gt;"metric.metricName"&lt;/span&gt;
      &lt;span class="na"&gt;[value]=&lt;/span&gt;&lt;span class="s"&gt;"metric.value"&lt;/span&gt;
      &lt;span class="na"&gt;[timestamp]=&lt;/span&gt;&lt;span class="s"&gt;"metric.timestamp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/app-live-chart&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to add &lt;code&gt;LiveChartComponent&lt;/code&gt; to the &lt;code&gt;imports&lt;/code&gt; array of &lt;code&gt;DashboardComponent&lt;/code&gt; since both are standalone.&lt;/p&gt;

&lt;p&gt;At this point, your dashboard displays both live numeric values and animated trend lines that update in real time as the backend pushes new data every two seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5.5: Alternative — Using ngx-charts (Advanced Option)
&lt;/h3&gt;

&lt;p&gt;If you prefer a more "Angular-native" charting library with declarative templates instead of imperative chart configuration objects, consider &lt;code&gt;@swimlane/ngx-charts&lt;/code&gt;. It integrates well with Angular's change detection and offers built-in chart types (line, bar, gauge, pie) ideal for dashboards. The setup process is similar: install the package, import the chart module, and bind data arrays to chart components via &lt;code&gt;[results]&lt;/code&gt; inputs. Many production dashboards mix both libraries depending on the visualization needed — for example, ngx-charts for gauges and Chart.js for dense time-series lines.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Part 6 (Intermediate): Styling the Dashboard
&lt;/h2&gt;

&lt;p&gt;A functional dashboard is only half the job — visual clarity matters, especially for monitoring tools that may be displayed on a large screen for extended periods (e.g., a NOC wall display).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6.1: Grid Layout
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;dashboard.component.scss&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.dashboard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#0f172a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#e2e8f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Inter'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.metric-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto-fit&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1fr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.chart-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto-fit&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;320px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1fr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6.2: Card Styling
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;metric-card.component.scss&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.metric-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#1e293b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.2s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-2px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;h3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.85rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.05em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#94a3b8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#3f8cff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;small&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#64748b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6.3: Visual Feedback on Update
&lt;/h3&gt;

&lt;p&gt;A nice UX touch for live dashboards is briefly highlighting a value when it changes, so users can visually notice an update without staring at numbers. You can achieve this with a CSS animation triggered by an Angular class binding tied to a "just updated" flag, toggled with a short &lt;code&gt;setTimeout&lt;/code&gt; whenever new data arrives. This kind of micro-interaction significantly improves the perceived "liveness" of the dashboard.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Part 7 (Advanced): Handling Reconnection and Error States
&lt;/h2&gt;

&lt;p&gt;Production dashboards must gracefully handle network interruptions. SignalR's &lt;code&gt;.withAutomaticReconnect()&lt;/code&gt; handles basic reconnection, but you should also surface connection status to users and handle edge cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7.1: Expose Connection State
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;signalr.service.ts&lt;/code&gt; to track and expose connection status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;connectionState$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reconnecting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;startConnection&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;signalR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HubConnectionBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiBaseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/hubs/dashboard`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withAutomaticReconnect&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onreconnecting&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionState$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reconnecting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onreconnected&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionState$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onclose&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionState$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionState$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionState$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerHandlers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The array &lt;code&gt;[0, 2000, 5000, 10000, 30000]&lt;/code&gt; passed to &lt;code&gt;withAutomaticReconnect&lt;/code&gt; defines custom retry delays (in milliseconds): retry immediately, then after 2s, 5s, 10s, and 30s, after which SignalR stops retrying automatically (you can add logic to manually retry beyond this).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7.2: Display a Connection Banner
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;dashboard.component.html&lt;/code&gt;, add a status banner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"connection-banner"&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"connectionState !== 'connected'"&lt;/span&gt; &lt;span class="na"&gt;[ngClass]=&lt;/span&gt;&lt;span class="s"&gt;"connectionState"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"connectionState === 'reconnecting'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Reconnecting to live data...&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"connectionState === 'disconnected'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Disconnected. Attempting to reconnect...&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subscribe to &lt;code&gt;connectionState$&lt;/code&gt; in the component class just like you did for metrics. This small addition dramatically improves trust in the dashboard — users immediately know if the data they're looking at might be stale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7.3: Handle Manual Reconnection
&lt;/h3&gt;

&lt;p&gt;If automatic reconnection exhausts its retries, implement a manual fallback, for instance using &lt;code&gt;setInterval&lt;/code&gt; to attempt &lt;code&gt;hubConnection.start()&lt;/code&gt; again every 30 seconds while in the &lt;code&gt;disconnected&lt;/code&gt; state, with exponential backoff capped at a reasonable maximum to avoid hammering the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7.4: Graceful Degradation
&lt;/h3&gt;

&lt;p&gt;Consider what happens if SignalR/WebSockets are entirely blocked (e.g., by a restrictive corporate proxy). SignalR automatically falls back to Server-Sent Events or long polling, but if you want a guaranteed fallback, you could implement a periodic REST polling mechanism as a last resort, triggered only when the &lt;code&gt;connectionState$&lt;/code&gt; remains &lt;code&gt;disconnected&lt;/code&gt; for an extended period.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Part 8 (Advanced): Adding Authentication with JWT
&lt;/h2&gt;

&lt;p&gt;Most production dashboards display sensitive business data and need to be secured. Here's how to add JWT-based authentication to both the backend and the SignalR connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8.1: Backend — Add JWT Authentication
&lt;/h3&gt;

&lt;p&gt;Install the JWT package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure it in &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Authentication.JwtBearer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.IdentityModel.Tokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;jwtKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Jwt:Key"&lt;/span&gt;&lt;span class="p"&gt;]!;&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JwtBearerDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJwtBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TokenValidationParameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;TokenValidationParameters&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ValidateIssuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidateAudience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidateLifetime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidateIssuerSigningKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ValidIssuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Jwt:Issuer"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;ValidAudience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Jwt:Audience"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;IssuerSigningKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SymmetricSecurityKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwtKey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// Required so SignalR can read the access token from the query string,&lt;/span&gt;
        &lt;span class="c1"&gt;// since WebSocket connections cannot set custom Authorization headers.&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JwtBearerEvents&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;OnMessageReceived&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWithSegments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hubs"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthentication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secure the hub and controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardHub&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hub&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MetricsController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important detail:&lt;/strong&gt; Browsers cannot attach custom &lt;code&gt;Authorization&lt;/code&gt; headers to native WebSocket connections, so SignalR's JavaScript client sends the token as a query string parameter (&lt;code&gt;access_token&lt;/code&gt;) instead. The &lt;code&gt;OnMessageReceived&lt;/code&gt; event above reads that token specifically for requests to the &lt;code&gt;/hubs&lt;/code&gt; path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8.2: Create a Simple Login Endpoint (for demo purposes)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AuthController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;FromBody&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;LoginRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Replace with real user validation against your identity store&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"demo"&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Unauthorized&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SymmetricSecurityKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Jwt:Key"&lt;/span&gt;&lt;span class="p"&gt;]!));&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SigningCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SecurityAlgorithms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HmacSha256&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;JwtSecurityToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Jwt:Issuer"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Jwt:Audience"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;signingCredentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;JwtSecurityTokenHandler&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;WriteToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;LoginRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the corresponding &lt;code&gt;Jwt:Key&lt;/code&gt;, &lt;code&gt;Jwt:Issuer&lt;/code&gt;, and &lt;code&gt;Jwt:Audience&lt;/code&gt; values to &lt;code&gt;appsettings.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8.3: Angular — Send the Token with SignalR
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;signalr.service.ts&lt;/code&gt; to pass the access token when building the connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hubConnection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;signalR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HubConnectionBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiBaseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/hubs/dashboard`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;accessTokenFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authToken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withAutomaticReconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8.4: Add an Angular HTTP Interceptor for REST Calls
&lt;/h3&gt;

&lt;p&gt;To attach the token to your regular REST API calls (like &lt;code&gt;/api/metrics/snapshot&lt;/code&gt;), add an &lt;code&gt;HttpInterceptorFn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpInterceptorFn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authInterceptor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpInterceptorFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authToken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cloned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;setHeaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cloned&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register it in &lt;code&gt;app.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;provideHttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withInterceptors&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;authInterceptor&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8.5: Build a Minimal Login Component
&lt;/h3&gt;

&lt;p&gt;Create a simple login form that calls &lt;code&gt;/api/auth/login&lt;/code&gt;, stores the returned token in &lt;code&gt;localStorage&lt;/code&gt; (or, for better security, a memory-based store with refresh tokens in an HttpOnly cookie for production-grade apps), and redirects to the dashboard route. Use an Angular route guard (&lt;code&gt;CanActivateFn&lt;/code&gt;) to prevent unauthenticated access to the dashboard route.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security note:&lt;/strong&gt; Storing JWTs in &lt;code&gt;localStorage&lt;/code&gt; is convenient but vulnerable to XSS attacks. For production systems handling sensitive data, consider using HttpOnly, Secure cookies combined with short-lived access tokens and a refresh token rotation strategy.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Part 9 (Advanced): Scaling SignalR with Redis Backplane
&lt;/h2&gt;

&lt;p&gt;If you deploy your ASP.NET Core backend across multiple server instances (for load balancing or high availability), SignalR connections will be distributed across those instances. By default, a message broadcast from one server instance won't reach clients connected to a different instance. This is solved using a &lt;strong&gt;backplane&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9.1: Install the Redis Backplane Package
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.AspNetCore.SignalR.StackExchangeRedis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 9.2: Configure It in Program.cs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSignalR&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddStackExchangeRedis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Redis:ConnectionString"&lt;/span&gt;&lt;span class="p"&gt;]!,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChannelPrefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StackExchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedisChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LiveDashboard"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this in place, when &lt;code&gt;MetricBroadcastService&lt;/code&gt; calls &lt;code&gt;Clients.All.SendAsync(...)&lt;/code&gt; on one server instance, Redis ensures the message is relayed to all other instances, which then forward it to their own connected clients. This makes your dashboard horizontally scalable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9.3: When Do You Need This?
&lt;/h3&gt;

&lt;p&gt;For a single-instance deployment (most small to mid-size applications), you do &lt;strong&gt;not&lt;/strong&gt; need a backplane. Only add this complexity once you're running multiple instances behind a load balancer — typically in higher-traffic production environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. Part 10 (Advanced): Performance Optimization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 10.1: Throttle High-Frequency Updates
&lt;/h3&gt;

&lt;p&gt;If your real data source produces updates faster than the UI needs to render them (e.g., hundreds of updates per second), avoid pushing every single update to clients. Instead, batch or throttle on the backend — for example, aggregate updates and broadcast at a fixed interval (every 500ms–1s) using a buffer, rather than sending on every change.&lt;/p&gt;

&lt;p&gt;On the Angular side, you can also throttle using RxJS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;throttleTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signalrService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metric$&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upsertMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 10.2: Use OnPush Change Detection
&lt;/h3&gt;

&lt;p&gt;For dashboard components receiving frequent updates, switch to &lt;code&gt;ChangeDetectionStrategy.OnPush&lt;/code&gt; to avoid unnecessary re-renders across the entire component tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-metric-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Combined with immutable data patterns (creating new objects/arrays rather than mutating existing ones, as we did in the chart component), &lt;code&gt;OnPush&lt;/code&gt; significantly reduces CPU usage on busy dashboards.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 10.3: Limit Chart History
&lt;/h3&gt;

&lt;p&gt;Capping chart data to a fixed window (we used &lt;code&gt;maxPoints = 30&lt;/code&gt; earlier) prevents memory growth and keeps rendering fast, especially important if the dashboard runs unattended for hours or days (e.g., on an office wall display).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 10.4: Use trackBy for *ngFor
&lt;/h3&gt;

&lt;p&gt;When rendering lists of metric cards or charts, always use &lt;code&gt;trackBy&lt;/code&gt; to prevent Angular from destroying and recreating DOM elements unnecessarily on every update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;app-metric-card&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let metric of metrics; trackBy: trackByName"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&amp;lt;/app-metric-card&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;trackByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MetricData&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 10.5: Compress SignalR Messages (Advanced)
&lt;/h3&gt;

&lt;p&gt;For very data-heavy dashboards, consider using MessagePack instead of the default JSON protocol for SignalR, which reduces payload size and improves throughput:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.AspNetCore.SignalR.Protocols.MessagePack
npm &lt;span class="nb"&gt;install&lt;/span&gt; @microsoft/signalr-protocol-msgpack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure it on both server (&lt;code&gt;AddSignalR().AddMessagePackProtocol()&lt;/code&gt;) and client (&lt;code&gt;.withHubProtocol(new MessagePackHubProtocol())&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  15. Part 11 (Advanced): Testing the Application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 11.1: Backend Unit Tests
&lt;/h3&gt;

&lt;p&gt;Use xUnit to test your services in isolation. For example, test that &lt;code&gt;DataGeneratorService.GenerateMetric&lt;/code&gt; returns values within the expected variance range:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GenerateMetric_ReturnsValueWithinVarianceRange&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DataGeneratorService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateMetric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CPU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CPU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MetricName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 11.2: Integration Testing the Hub
&lt;/h3&gt;

&lt;p&gt;ASP.NET Core supports integration testing using &lt;code&gt;WebApplicationFactory&lt;/code&gt; combined with a real SignalR client connecting to an in-memory test server, allowing you to verify that messages broadcast from the hub are actually received by a connected client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 11.3: Angular Unit Tests
&lt;/h3&gt;

&lt;p&gt;Use Jasmine/Karma (or Jest, if configured) to test that &lt;code&gt;MetricsService&lt;/code&gt; correctly upserts metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should update existing metric value on new data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upsertMetric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]({&lt;/span&gt; &lt;span class="na"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CPU&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-01-01T00:00:00Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upsertMetric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]({&lt;/span&gt; &lt;span class="na"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CPU&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-01-01T00:00:05Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metrics$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cpu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metricName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CPU&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 11.4: End-to-End Testing
&lt;/h3&gt;

&lt;p&gt;Use Playwright or Cypress to verify the full flow: load the dashboard, confirm the initial snapshot renders, wait for a live update, and assert that displayed values change without a page reload.&lt;/p&gt;




&lt;h2&gt;
  
  
  16. Part 12 (Advanced): Deploying to Production
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 16.1: Build the Angular App for Production
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng build &lt;span class="nt"&gt;--configuration&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This outputs static files to &lt;code&gt;dist/live-dashboard-client&lt;/code&gt;, which can be served by any static file host or reverse proxy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 16.2: Publish the ASP.NET Core Backend
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet publish &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; ./publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 16.3: Dockerize Both Applications
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Backend Dockerfile:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:8.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet publish &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; /app/publish

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/dotnet/aspnet:8.0&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/publish .&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "LiveDashboard.Api.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend Dockerfile (served via Nginx):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--configuration&lt;/span&gt; production

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/dist/live-dashboard-client /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A minimal &lt;code&gt;nginx.conf&lt;/code&gt; should include a WebSocket-friendly proxy configuration if Nginx also handles routing to the backend, ensuring &lt;code&gt;Upgrade&lt;/code&gt; and &lt;code&gt;Connection&lt;/code&gt; headers are forwarded correctly for SignalR's WebSocket transport.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 16.4: docker-compose for Local Orchestration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./LiveDashboard.Api&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:80"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ASPNETCORE_ENVIRONMENT=Production&lt;/span&gt;

  &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./live-dashboard-client&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4200:80"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 16.5: Deploying to Azure
&lt;/h3&gt;

&lt;p&gt;For Azure App Service deployments, deploy the ASP.NET Core backend to an &lt;strong&gt;App Service&lt;/strong&gt; (with &lt;strong&gt;WebSockets enabled&lt;/strong&gt; in the Configuration settings — this is required for SignalR to function correctly) and the Angular frontend to &lt;strong&gt;Azure Static Web Apps&lt;/strong&gt; or a separate App Service serving static files. If you expect multiple backend instances, enable &lt;strong&gt;Azure SignalR Service&lt;/strong&gt; instead of self-hosting SignalR, which handles scaling and connection management for you and removes the need for a Redis backplane.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 16.6: Deploying to IIS
&lt;/h3&gt;

&lt;p&gt;If deploying to IIS, ensure the &lt;strong&gt;WebSocket Protocol&lt;/strong&gt; feature is enabled in Windows Server roles/features, and that the &lt;strong&gt;ASP.NET Core Module v2&lt;/strong&gt; is installed. Without WebSocket support enabled at the IIS/server level, SignalR will silently fall back to long polling, which still works but is less efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 16.7: HTTPS and Security Headers
&lt;/h3&gt;

&lt;p&gt;In production, always serve both frontend and backend over HTTPS — mixed content (HTTPS frontend calling an HTTP backend) will be blocked by browsers, and WebSocket connections (&lt;code&gt;wss://&lt;/code&gt;) require a secure context when the page itself is loaded over HTTPS.&lt;/p&gt;




&lt;h2&gt;
  
  
  17. Common Errors and Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Error: "Failed to complete negotiation with the server"&lt;/strong&gt;&lt;br&gt;
Usually caused by CORS misconfiguration or the backend not running. Confirm &lt;code&gt;AllowCredentials()&lt;/code&gt; is set and that the origin matches exactly (including protocol and port).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error: SignalR connects but no data arrives&lt;/strong&gt;&lt;br&gt;
Double-check that the event name in &lt;code&gt;hubConnection.on('ReceiveMetric', ...)&lt;/code&gt; exactly matches the string used in &lt;code&gt;SendAsync("ReceiveMetric", ...)&lt;/code&gt; on the backend — these are case-sensitive string matches, not type-checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error: Connection works locally but fails after deployment&lt;/strong&gt;&lt;br&gt;
Almost always a missing WebSocket configuration at the hosting/proxy layer (IIS, Nginx, or a load balancer not configured for WebSocket passthrough).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Charts not updating despite new data arriving&lt;/strong&gt;&lt;br&gt;
Verify you are creating new array/object references when updating chart data (as shown in Step 5.3) rather than mutating existing objects in place — Angular and Chart.js both rely on reference changes to detect updates efficiently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;401 Unauthorized when connecting to the hub after adding JWT auth&lt;/strong&gt;&lt;br&gt;
Confirm the &lt;code&gt;accessTokenFactory&lt;/code&gt; is correctly returning a valid, non-expired token, and that the &lt;code&gt;OnMessageReceived&lt;/code&gt; handler on the backend is scoped to the &lt;code&gt;/hubs&lt;/code&gt; path.&lt;/p&gt;




&lt;h2&gt;
  
  
  18. Best Practices Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use SignalR for live updates; use REST only for the initial data snapshot.&lt;/li&gt;
&lt;li&gt;Always implement &lt;code&gt;.withAutomaticReconnect()&lt;/code&gt; and surface connection status in the UI.&lt;/li&gt;
&lt;li&gt;Cap historical data arrays to avoid unbounded memory growth in long-running dashboard sessions.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;OnPush&lt;/code&gt; change detection and &lt;code&gt;trackBy&lt;/code&gt; for performance on data-heavy dashboards.&lt;/li&gt;
&lt;li&gt;Secure both the REST API and the SignalR hub with &lt;code&gt;[Authorize]&lt;/code&gt; once authentication is added.&lt;/li&gt;
&lt;li&gt;Enable WebSockets explicitly on your hosting environment (IIS, Azure App Service, Nginx).&lt;/li&gt;
&lt;li&gt;Use a Redis backplane or Azure SignalR Service only once you scale beyond a single backend instance.&lt;/li&gt;
&lt;li&gt;Throttle extremely high-frequency data sources before broadcasting to clients.&lt;/li&gt;
&lt;li&gt;Always test reconnection behavior by manually killing and restarting the backend during development.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  19. Frequently Asked Questions (FAQ)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Do I need SignalR, or can I just poll the API every few seconds?&lt;/strong&gt;&lt;br&gt;
Polling works for simple cases but is less efficient and introduces latency. SignalR provides true push-based real-time updates with lower overhead once a connection is established, making it the better choice for dashboards needing sub-second responsiveness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I use this approach with React or Vue instead of Angular?&lt;/strong&gt;&lt;br&gt;
Yes. The backend (ASP.NET Core + SignalR) is frontend-agnostic. Microsoft provides the same &lt;code&gt;@microsoft/signalr&lt;/code&gt; JavaScript client that works with any frontend framework, not just Angular.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What's the difference between SignalR and raw WebSockets?&lt;/strong&gt;&lt;br&gt;
SignalR is a higher-level abstraction built on top of WebSockets (with automatic fallback to other transports). It handles connection management, reconnection, and message serialization for you, whereas raw WebSockets require you to build all of that yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How do I handle dashboards with many different data types (not just numeric metrics)?&lt;/strong&gt;&lt;br&gt;
Use a more generic message envelope (e.g., a &lt;code&gt;DashboardEvent&lt;/code&gt; with a &lt;code&gt;type&lt;/code&gt; field and a polymorphic payload) instead of a single &lt;code&gt;MetricData&lt;/code&gt; model, and route messages to different handlers/components based on &lt;code&gt;type&lt;/code&gt; on the client side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Is SignalR suitable for very high-frequency data (thousands of updates per second)?&lt;/strong&gt;&lt;br&gt;
For extremely high-frequency data, consider batching updates on the server before broadcasting (as discussed in the performance section) rather than sending every single data point individually.&lt;/p&gt;




&lt;h2&gt;
  
  
  20. Conclusion
&lt;/h2&gt;

&lt;p&gt;You've now built a complete, real-time live data dashboard from the ground up — starting with a simple ASP.NET Core Web API, layering in SignalR for true real-time push updates, building a reactive Angular frontend with live-updating charts, and progressing through advanced topics like authentication, scaling, performance optimization, and production deployment.&lt;/p&gt;

&lt;p&gt;This architecture pattern — REST for initial state, SignalR for live updates, RxJS for reactive state management on the client — is a robust foundation you can extend to almost any real-time use case: IoT monitoring, financial dashboards, server health monitors, live order tracking, or collaborative applications.&lt;/p&gt;

&lt;p&gt;From here, consider exploring related topics such as integrating a real database with Entity Framework Core instead of simulated data, adding role-based dashboards where different users see different metrics, or building alerting rules that trigger notifications when metric thresholds are crossed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this guide helpful, consider bookmarking it as a reference architecture for your next real-time Angular + ASP.NET Core project.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>core</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Complete Guide to Types of Testing in Software Engineering</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Sun, 07 Jun 2026 13:49:25 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/the-complete-guide-to-types-of-testing-in-software-engineering-3lmj</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/the-complete-guide-to-types-of-testing-in-software-engineering-3lmj</guid>
      <description>&lt;p&gt;Software testing is primarily divided into two main categories: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Functional Testing&lt;/strong&gt;, which verifies what the system does against business requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-Functional Testing&lt;/strong&gt;, which evaluates how the system performs regarding usability, speed, and security. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These categories are executed across different stages, methods, and levels to ensure the final product is stable and reliable.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Functional Testing (The "What")
&lt;/h2&gt;

&lt;p&gt;Functional testing focuses on the business requirements of an application, validating that every feature operates according to specified expectations. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit Testing:&lt;/strong&gt; Checks individual blocks, methods, or functions of code in strict isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration Testing:&lt;/strong&gt; Verifies that different modules or units work together seamlessly when combined.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System Testing:&lt;/strong&gt; Evaluates the complete, fully integrated software system as a whole to check overall business compliance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acceptance Testing (UAT):&lt;/strong&gt; Conducted by clients or end-users to confirm if the application is ready for real-world deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regression Testing:&lt;/strong&gt; Re-tests existing features after updates or code modifications to ensure no new bugs were introduced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smoke Testing:&lt;/strong&gt; Runs rapid, high-level checks on core functionalities to verify if a build is stable enough for deeper testing. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Non-Functional Testing (The "How")
&lt;/h2&gt;

&lt;p&gt;Non-functional testing evaluates the quality, technical behaviors, and operational readiness of the software rather than specific features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance Testing:&lt;/strong&gt; Measures system responsiveness, speed, and resource usage under normal and peak conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Testing:&lt;/strong&gt; Evaluates how the application behaves under a specific, expected volume of simultaneous users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stress Testing:&lt;/strong&gt; Pushes the system past its normal performance capacity to find its breaking point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Testing:&lt;/strong&gt; Scans the application for internal or external vulnerabilities and cyber threats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usability Testing:&lt;/strong&gt; Assesses user experience, interface layout, and overall user-friendliness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility Testing:&lt;/strong&gt; Verifies the software functions correctly across various operating systems, browsers, and devices. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Testing Approaches (The "Knowledge")
&lt;/h2&gt;

&lt;p&gt;Software tests are also classified by how much visibility the tester has into the underlying programming code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Black-Box Testing:&lt;/strong&gt; Testing is done completely from the outside, with no knowledge of the internal code or logic structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;White-Box Testing:&lt;/strong&gt; Testing is executed with deep knowledge of the code, loops, data flows, and internal architectures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grey-Box Testing:&lt;/strong&gt; A hybrid approach where the tester has limited or partial knowledge of the internal system backend.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Methods of Execution
&lt;/h2&gt;

&lt;p&gt;Depending on the engineering team's goals and resources, the testing types above can be carried out through two primary execution methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manual Testing:&lt;/strong&gt; Human QA specialists manually interact with the user interface and tools to log unexpected defects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Testing:&lt;/strong&gt; Writing scripts and utilizing testing frameworks to execute repetitive test suites with speed and precision.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Keep coding.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>softwareengineering</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Implement SSE for Notifications in .NET in a Production-Ready Way?</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Thu, 16 Apr 2026 02:43:20 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/how-to-implement-sse-for-notifications-in-net-in-a-production-ready-way-4a9i</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/how-to-implement-sse-for-notifications-in-net-in-a-production-ready-way-4a9i</guid>
      <description>&lt;p&gt;This is &lt;strong&gt;not a demo&lt;/strong&gt; – this is the &lt;strong&gt;same pattern used in real chat/notification systems&lt;/strong&gt; (WhatsApp-like browser notifications, dashboards, etc.).&lt;/p&gt;

&lt;h2&gt;
  
  
  1. When to Use SSE (Production Decision)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.mindstick.com/interview/34423/what-is-sse" rel="noopener noreferrer"&gt;Use &lt;strong&gt;SSE&lt;/strong&gt;&lt;/a&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  One-way server → browser notifications&lt;/li&gt;
&lt;li&gt;  Chat message alerts (not full chat transport)&lt;/li&gt;
&lt;li&gt;  Live counters, alerts, status updates&lt;/li&gt;
&lt;li&gt;  Browser-based apps (no mobile socket requirement)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do &lt;strong&gt;NOT&lt;/strong&gt; use SSE for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Binary streaming&lt;/li&gt;
&lt;li&gt;  High-frequency bidirectional chat typing events&lt;/li&gt;
&lt;li&gt;  Mobile background connections (WebSocket better)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Architecture (Production)
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser (EventSource)
        ↓
SSE Controller (long-lived HTTP)
        ↓
ConcurrentDictionary&amp;lt;UserId, SseClient&amp;gt;
        ↓
Notification Publisher (Chat / Job / API)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Key rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;One&lt;/strong&gt; &lt;a href="https://answers.mindstick.com/qa/116201/how-to-implement-the-sse-in-dot-net" rel="noopener noreferrer"&gt;&lt;strong&gt;SSE connection&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;per user&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;No DB polling&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;No blocking threads&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Heartbeat required&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Auto-reconnect handled by browser&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Core SSE Infrastructure (IMPORTANT)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 SSE Client Model
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class SseClient
{
    public int UserId { get; set; }
    public HttpResponse Response { get; set; }
    public DateTime ConnectedAt { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  3.2 SSE Connection Manager (Thread-Safe)
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static class SseConnectionManager
{
    private static readonly ConcurrentDictionary&amp;lt;int, SseClient&amp;gt; _clients
        = new ConcurrentDictionary&amp;lt;int, SseClient&amp;gt;();

    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);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;  Thread-safe&lt;/li&gt;
&lt;li&gt;  Memory-safe&lt;/li&gt;
&lt;li&gt;  Handles broken connections&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. SSE Controller (Production-Safe)
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Why heartbeat?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Prevents proxy/load-balancer timeout&lt;/li&gt;
&lt;li&gt;  Detects dead connections&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  5. Sending Notifications (Chat / API / Jobs)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Example: Chat Message Notification&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class NotificationService
{
    public static void NotifyUser(int userId, object payload)
    {
        string json = Newtonsoft.Json.JsonConvert.SerializeObject(payload);
        SseConnectionManager.SendToUser(userId, "message", json);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Usage:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NotificationService.NotifyUser(671496, new
{
    fromUserId = 655093,
    text = "Hello 👋",
    createdAt = DateTime.UtcNow
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  6. Browser Client (Production JS)
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
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...");
};
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;  Auto-reconnect handled by browser&lt;/li&gt;
&lt;li&gt;  No polling&lt;/li&gt;
&lt;li&gt;  Low memory footprint&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Multi-Tab Handling (WhatsApp-Like Behavior)
&lt;/h2&gt;

&lt;p&gt;If &lt;strong&gt;same user opens multiple tabs&lt;/strong&gt;, enforce &lt;strong&gt;single active tab&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Other tabs will automatically disconnect and can show popup:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This account is active in another tab”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  8. Production Hardening Checklist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;IIS / Hosting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Disable response buffering&lt;/li&gt;
&lt;li&gt;  Increase request timeout&lt;/li&gt;
&lt;li&gt;  Enable keep-alive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Auth-based user binding&lt;/li&gt;
&lt;li&gt;  No userId in query string&lt;/li&gt;
&lt;li&gt;  Rate-limit notification sends&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  In-memory only → &lt;strong&gt;single server&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Multi-server → use &lt;strong&gt;Redis Pub/Sub&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. Multi-Server (Redis) Architecture
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server A  ─┐
Server B  ─┼── Redis Pub/Sub ──► SSE Clients
Server C  ─┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Each server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Subscribes to Redis channel&lt;/li&gt;
&lt;li&gt;  Pushes events to connected users&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10. Why Big Companies Use SSE
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  GitHub notifications&lt;/li&gt;
&lt;li&gt;  Slack web notifications&lt;/li&gt;
&lt;li&gt;  Trading dashboards&lt;/li&gt;
&lt;li&gt;  Monitoring systems&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;  Lower cost than WebSockets&lt;/li&gt;
&lt;li&gt;  Works perfectly behind proxies&lt;/li&gt;
&lt;li&gt;  Auto-reconnect built-in&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  11. SSE vs WebSocket (Quick Decision)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;SSE&lt;/th&gt;
&lt;th&gt;WebSocket&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Direction&lt;/td&gt;
&lt;td&gt;Server → Client&lt;/td&gt;
&lt;td&gt;Two-way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Harder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser support&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chat notifications&lt;/td&gt;
&lt;td&gt;Best&lt;/td&gt;
&lt;td&gt;Overkill&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Final Recommendation (For Your Chat App)
&lt;/h2&gt;

&lt;p&gt;Use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SSE&lt;/strong&gt; → notifications, unread count, alerts&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;API&lt;/strong&gt; → send message&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Optional WebSocket&lt;/strong&gt; → typing indicators only&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Read Also&lt;/strong&gt;&amp;nbsp;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.mindstick.com/interview/34423/what-is-sse" rel="noopener noreferrer"&gt;&lt;strong&gt;What is SSE?&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://www.mindstick.com/interview/34422/how-to-implement-websocket-ap" rel="noopener noreferrer"&gt;&lt;strong&gt;How to implement Websocket AP?&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>backend</category>
      <category>dotnet</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>EmailSpamChecker class for .NET Framework 4.7</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Wed, 18 Jun 2025 16:53:15 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/emailspamchecker-class-for-net-framework-47-59h8</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/emailspamchecker-class-for-net-framework-47-59h8</guid>
      <description>&lt;p&gt;Here’s a complete and reusable EmailSpamChecker class for .NET Framework 4.7, which performs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax validation&lt;/li&gt;
&lt;li&gt;Blacklisted domain check&lt;/li&gt;
&lt;li&gt;MX record verification (requires DnsClient NuGet)&lt;/li&gt;
&lt;li&gt;Scoring logic for spam suspicion&lt;/li&gt;
&lt;li&gt;Customizable domain block list&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Step 1: Install DnsClient NuGet Package
&lt;/h3&gt;

&lt;p&gt;From NuGet Package Manager Console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package DnsClient
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2: EmailSpamChecker Class
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.Text.RegularExpressions;
using DnsClient;

public class EmailSpamChecker
{
    private static readonly string[] _blacklistedDomains = new[]
    {
        "mailinator.com", "10minutemail.com", "guerrillamail.com",
        "tempmail.com", "desy.xyz", "yopmail.com", "trashmail.com"
    };

    private static readonly string[] _suspiciousTlds = new[]
    {
        ".xyz", ".tk", ".gq", ".ml", ".cf"
    };

    public class SpamCheckResult
    {
        public bool IsValid { get; set; }
        public int SpamScore { get; set; }
        public List&amp;lt;string&amp;gt; Issues { get; set; } = new List&amp;lt;string&amp;gt;();
    }

    public static SpamCheckResult CheckEmail(string email)
    {
        var result = new SpamCheckResult();

        // Check basic syntax
        if (!IsValidSyntax(email))
        {
            result.Issues.Add("Invalid email syntax.");
            result.SpamScore += 2;
            result.IsValid = false;
            return result;
        }

        string domain = email.Split('@')[1].ToLower();

        // Check blacklist
        if (_blacklistedDomains.Contains(domain))
        {
            result.Issues.Add("Domain is blacklisted.");
            result.SpamScore += 3;
        }

        // Check suspicious TLD
        if (_suspiciousTlds.Any(tld =&amp;gt; domain.EndsWith(tld)))
        {
            result.Issues.Add("Suspicious top-level domain.");
            result.SpamScore += 1;
        }

        // Check if it looks random (e.g., xas21@abc.xyz)
        if (IsRandomLooking(email))
        {
            result.Issues.Add("Username looks auto-generated or random.");
            result.SpamScore += 1;
        }

        // Check MX record
        if (!HasValidMxRecord(domain))
        {
            result.Issues.Add("Domain has no MX record (may not receive emails).");
            result.SpamScore += 2;
        }

        result.IsValid = result.SpamScore &amp;lt; 3;
        return result;
    }

    private static bool IsValidSyntax(string email)
    {
        try
        {
            var addr = new MailAddress(email);
            return addr.Address == email;
        }
        catch
        {
            return false;
        }
    }

    private static bool HasValidMxRecord(string domain)
    {
        try
        {
            var lookup = new LookupClient();
            var result = lookup.Query(domain, QueryType.MX);
            return result.Answers.MxRecords().Any();
        }
        catch
        {
            return false;
        }
    }

    private static bool IsRandomLooking(string email)
    {
        string user = email.Split('@')[0];

        // Count digits and special chars
        int digitCount = user.Count(char.IsDigit);
        int symbolCount = user.Count(c =&amp;gt; !char.IsLetterOrDigit(c));

        // Very short or too random
        return user.Length &amp;lt; 5 || digitCount + symbolCount &amp;gt; 4;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Usage Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string inputEmail = "xyz@desy.xyz";
var result = EmailSpamChecker.CheckEmail(inputEmail);

if (!result.IsValid)
{
    Console.WriteLine("Potential spam or invalid email:");
    foreach (var issue in result.Issues)
        Console.WriteLine($"- {issue}");
}
else
{
    Console.WriteLine("Email looks clean.");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Output Example
&lt;/h3&gt;

&lt;p&gt;Potential spam or invalid email:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain is blacklisted.&lt;/li&gt;
&lt;li&gt;Suspicious top-level domain.&lt;/li&gt;
&lt;li&gt;Domain has no MX record (may not receive emails).&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks&lt;br&gt;
Keep Coding.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Clean Architecture in .net application step by step</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Sat, 12 Apr 2025 13:54:40 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/clean-architecture-in-net-application-step-by-step-2ol0</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/clean-architecture-in-net-application-step-by-step-2ol0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Clean Architecture&lt;/strong&gt; is a software design philosophy introduced by &lt;strong&gt;Robert C. Martin (Uncle Bob)&lt;/strong&gt;. Its goal is to create systems that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Independent of frameworks&lt;/li&gt;
&lt;li&gt;Testable&lt;/li&gt;
&lt;li&gt;Independent of UI or database&lt;/li&gt;
&lt;li&gt;Flexible and maintainable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Principles
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Separation of Concerns&lt;/strong&gt;: Each layer of the application has a specific responsibility and doesn't mix with others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency Rule&lt;/strong&gt;: Code dependencies must point inward, from outer layers (UI, DB) to inner layers (Use Cases, Entities). Inner layers know nothing about the outer layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inversion of Control&lt;/strong&gt;: High-level policies (business rules) shouldn't depend on low-level details (DB, APIs). Instead, interfaces should be used to invert control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Independence&lt;/strong&gt;: 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework Independent&lt;/strong&gt;: Can change from ASP.NET to another without affecting core logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Independent&lt;/strong&gt;: Can switch from SQL Server to MongoDB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Independent&lt;/strong&gt;: Can change from Web to Mobile easily.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Layers
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Domain Layer (Entities)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Contains core business rules&lt;/li&gt;
&lt;li&gt;Framework and UI agnostic&lt;/li&gt;
&lt;li&gt;Pure business objects (e.g., User, Order)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Layer (Use Cases)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Contains application-specific logic&lt;/li&gt;
&lt;li&gt;Coordinates the flow of data&lt;/li&gt;
&lt;li&gt;Interfaces with repositories&lt;/li&gt;
&lt;li&gt;E.g., CreateUser, GetOrders&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface Adapters Layer&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Adapts data between use cases and frameworks&lt;/li&gt;
&lt;li&gt;Includes Controllers, Gateways, ViewModels&lt;/li&gt;
&lt;li&gt;Implements interfaces from Application Layer&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frameworks &amp;amp; Drivers&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;External tools and frameworks (e.g., ASP.NET Core, EF Core)&lt;/li&gt;
&lt;li&gt;Dependency injection and infrastructure&lt;/li&gt;
&lt;li&gt;Least stable and most replaceable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Flow of Control
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;UI (Controller) → Use Case → Domain Logic → Output/Result&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Controllers receive the input (e.g., HTTP request)&lt;/li&gt;
&lt;li&gt;Pass data to the Use Case&lt;/li&gt;
&lt;li&gt;Use Case performs business logic using the Domain&lt;/li&gt;
&lt;li&gt;Returns results (e.g., DTOs or ViewModels)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚖️ Advantages
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High Testability&lt;/strong&gt; – Business logic can be tested without UI or DB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to Maintain&lt;/strong&gt; – Changes in UI/DB won’t affect core logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;– Modular design helps teams work independently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;– Domain and Use Cases are reusable in different apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Violating the dependency rule (e.g., use case calling Entity Framework directly)&lt;/li&gt;
&lt;li&gt;Mixing business logic with framework code&lt;/li&gt;
&lt;li&gt;Skipping interfaces (tight coupling)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧱 Project Structure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: CleanArchitectureDemo.sln&lt;br&gt;
&lt;strong&gt;Projects&lt;/strong&gt;:&lt;br&gt;
├── CleanArchitectureDemo.Domain         // &lt;strong&gt;Entities&lt;/strong&gt;&lt;br&gt;
├── CleanArchitectureDemo.Application    // &lt;strong&gt;Use Cases&lt;/strong&gt;&lt;br&gt;
├── CleanArchitectureDemo.Infrastructure // &lt;strong&gt;DB / Repositories&lt;/strong&gt;&lt;br&gt;
├── CleanArchitectureDemo.API            // &lt;strong&gt;Web API (Controllers)&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🔧 Create Projects
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet new sln -n CleanArchitectureDemo

dotnet new classlib -n CleanArchitectureDemo.Domain
dotnet new classlib -n CleanArchitectureDemo.Application
dotnet new classlib -n CleanArchitectureDemo.Infrastructure
dotnet new webapi   -n CleanArchitectureDemo.API

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Add them to the solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet sln add CleanArchitectureDemo.Domain
dotnet sln add CleanArchitectureDemo.Application
dotnet sln add CleanArchitectureDemo.Infrastructure
dotnet sln add CleanArchitectureDemo.API

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add project references:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet add CleanArchitectureDemo.Application reference CleanArchitectureDemo.Domain
dotnet add CleanArchitectureDemo.Infrastructure reference CleanArchitectureDemo.Application
dotnet add CleanArchitectureDemo.API reference CleanArchitectureDemo.Application
dotnet add CleanArchitectureDemo.API reference CleanArchitectureDemo.Infrastructure

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📁 Layer Implementations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Domain/Entities/User.cs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace CleanArchitectureDemo.Domain.Entities
{
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Application/Interfaces/IUserRepository.cs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using CleanArchitectureDemo.Domain.Entities;
using System.Collections.Generic;

namespace CleanArchitectureDemo.Application.Interfaces
{
    public interface IUserRepository
    {
        IEnumerable&amp;lt;User&amp;gt; GetAll();
        User GetById(int id);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Application/UseCases/UserService.cs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using CleanArchitectureDemo.Application.Interfaces;
using CleanArchitectureDemo.Domain.Entities;
using System.Collections.Generic;

namespace CleanArchitectureDemo.Application.UseCases
{
    public class UserService
    {
        private readonly IUserRepository _userRepository;

        public UserService(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }

        public IEnumerable&amp;lt;User&amp;gt; GetUsers() =&amp;gt; _userRepository.GetAll();

        public User GetUser(int id) =&amp;gt; _userRepository.GetById(id);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Infrastructure/Repositories/InMemoryUserRepository.cs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using CleanArchitectureDemo.Application.Interfaces;
using CleanArchitectureDemo.Domain.Entities;
using System.Collections.Generic;
using System.Linq;

namespace CleanArchitectureDemo.Infrastructure.Repositories
{
    public class InMemoryUserRepository : IUserRepository
    {
        private readonly List&amp;lt;User&amp;gt; _users = new()
        {
            new User { Id = 1, Name = "Alice", Email = "alice@example.com" },
            new User { Id = 2, Name = "Bob", Email = "bob@example.com" }
        };

        public IEnumerable&amp;lt;User&amp;gt; GetAll() =&amp;gt; _users;

        public User GetById(int id) =&amp;gt; _users.FirstOrDefault(u =&amp;gt; u.Id == id);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. API/Controllers/UserController.cs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore.Mvc;
using CleanArchitectureDemo.Application.UseCases;
using CleanArchitectureDemo.Domain.Entities;
using System.Collections.Generic;

namespace CleanArchitectureDemo.API.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class UserController : ControllerBase
    {
        private readonly UserService _userService;

        public UserController(UserService userService)
        {
            _userService = userService;
        }

        [HttpGet]
        public IEnumerable&amp;lt;User&amp;gt; Get() =&amp;gt; _userService.GetUsers();

        [HttpGet("{id}")]
        public ActionResult&amp;lt;User&amp;gt; Get(int id)
        {
            var user = _userService.GetUser(id);
            if (user == null)
                return NotFound();
            return user;
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. API/Program.cs – Register Dependencies&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using CleanArchitectureDemo.Application.Interfaces;
using CleanArchitectureDemo.Application.UseCases;
using CleanArchitectureDemo.Infrastructure.Repositories;

var builder = WebApplication.CreateBuilder(args);

// Dependency Injection
builder.Services.AddScoped&amp;lt;IUserRepository, InMemoryUserRepository&amp;gt;();
builder.Services.AddScoped&amp;lt;UserService&amp;gt;();

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

app.MapControllers();
app.Run();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Thanks &lt;br&gt;
Keep Coding.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>dotnet</category>
      <category>cleancode</category>
      <category>cleancoding</category>
    </item>
    <item>
      <title>Reduce the page execution time</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Sun, 06 Apr 2025 18:32:50 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/reduce-the-time-of-page-execution-15gh</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/reduce-the-time-of-page-execution-15gh</guid>
      <description>&lt;p&gt;Alright, you're aiming for top-notch Core Web Vitals — crisp, smooth, fast-loading webpages that make Lighthouse go “WOW” and Google rank you like royalty 👑. Let’s break down the best practices to write HTML, CSS, JS, fonts, etc. that give you green scores across all Web Vitals:&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;HTML: Keep it Clean, Semantic &amp;amp; Lightweight&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset="UTF-8" /&amp;gt;
  &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1" /&amp;gt;
  &amp;lt;title&amp;gt;Your Awesome Page&amp;lt;/title&amp;gt;

  &amp;lt;!-- Preconnects for performance --&amp;gt;
  &amp;lt;link rel="preconnect" href="https://fonts.googleapis.com" /&amp;gt;
  &amp;lt;link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /&amp;gt;

  &amp;lt;!-- Inline Critical CSS --&amp;gt;
  &amp;lt;style&amp;gt;
    /* Only critical styles here (for above-the-fold content) */
    body {
      font-family: 'Inter', sans-serif;
      margin: 0;
      padding: 0;
      background: #fff;
      color: #333;
    }
    h1 {
      font-size: 2rem;
      text-align: center;
      margin-top: 2rem;
    }
  &amp;lt;/style&amp;gt;

  &amp;lt;!-- Load fonts properly --&amp;gt;
  &amp;lt;link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&amp;amp;display=swap" rel="stylesheet" /&amp;gt;

  &amp;lt;!-- Defer non-critical CSS --&amp;gt;
  &amp;lt;link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'" /&amp;gt;
  &amp;lt;noscript&amp;gt;&amp;lt;link rel="stylesheet" href="styles.css" /&amp;gt;&amp;lt;/noscript&amp;gt;

  &amp;lt;!-- Important: Avoid large JS in &amp;lt;head&amp;gt; --&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;

  &amp;lt;h1&amp;gt;Hello, Web Vitals!&amp;lt;/h1&amp;gt;
  &amp;lt;button id="btn"&amp;gt;Click Me&amp;lt;/button&amp;gt;

  &amp;lt;!-- Minimal Critical JS (inlined) --&amp;gt;
  &amp;lt;script&amp;gt;
    document.addEventListener('DOMContentLoaded', () =&amp;gt; {
      document.getElementById('btn').addEventListener('click', () =&amp;gt; {
        alert('Hi there!');
      });
    });
  &amp;lt;/script&amp;gt;

  &amp;lt;!-- Async/defer all non-critical JS --&amp;gt;
  &amp;lt;script src="main.js" defer&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;CSS: Optimize, Minify &amp;amp; Use Only What’s Needed&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract Critical CSS and inline it (manually or with tools like critical)&lt;/li&gt;
&lt;li&gt;Load the rest asynchronously (media="print" trick)&lt;/li&gt;
&lt;li&gt;Avoid unused styles (Lighthouse can show you these)&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;:where()&lt;/code&gt; or &lt;code&gt;:is()&lt;/code&gt; instead of long selector chains (faster parsing)&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;JavaScript: Split, Defer, and Delay&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inline only critical JS&lt;/li&gt;
&lt;li&gt;Use defer for everything else&lt;/li&gt;
&lt;li&gt;Use code splitting (in frameworks like React, Vue, etc.)&lt;/li&gt;
&lt;li&gt;Avoid large JS bundles — aim for &amp;lt;150KB (gzipped) for initial load&lt;/li&gt;
&lt;li&gt;Lazy-load expensive features like charts, sliders, chat widgets&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Fonts: Load Fast and Avoid Layout Shifts&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;font-display: swap&lt;/code&gt; (default with Google Fonts)&lt;/li&gt;
&lt;li&gt;Preconnect to &lt;code&gt;fonts.googleapis.com&lt;/code&gt; and &lt;code&gt;fonts.gstatic.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Prefer system fonts for super-fast load (optional)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&amp;amp;display=swap');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OR use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Images: Compress, Lazy Load, Responsive&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;img src="banner.jpg" loading="lazy" alt="Banner" width="600" height="400" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;loading="lazy"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add width and height attributes to prevent CLS (layout shift)&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;WebP&lt;/code&gt; or &lt;code&gt;AVIF&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;srcset&lt;/code&gt;for responsive images if needed&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Core Web Vitals Targets (What You’re Chasing)&lt;/strong&gt;&lt;br&gt;
Metric                          Goal&lt;br&gt;
LCP (Largest Contentful Paint)  &amp;lt; 2.5s&lt;br&gt;
FID (First Input Delay)         &amp;lt; 100ms&lt;br&gt;
CLS (Cumulative Layout Shift)   &amp;lt; 0.1&lt;br&gt;
TTI (Time to Interactive)   As fast as possible&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read More&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://dev.to/ravivis13370227/comprehensive-guide-to-documentation-comments-in-c-379d"&gt;Comprehensive Guide to Documentation Comments in C#&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/ravivis13370227/understanding-strong-password-regex-in-c-from-basic-to-advanced-25cf"&gt;Understanding Strong Password Regex in C#: From Basic to Advanced&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Thank You,&lt;br&gt;
Keep coding&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Understanding Strong Password Regex in C#: From Basic to Advanced</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Mon, 03 Feb 2025 13:09:57 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/understanding-strong-password-regex-in-c-from-basic-to-advanced-25cf</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/understanding-strong-password-regex-in-c-from-basic-to-advanced-25cf</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;In today’s digital age, ensuring password security is a critical aspect of software development. One way to enforce security standards is by using regular expressions (Regex) to validate passwords. In this article, we will explore different levels of password complexity, ranging from basic to advanced, using C#.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;1. Basic Password Validation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A basic password validation rule simply enforces a minimum length. The following Regex pattern ensures that a password is at least 8 characters long:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string basicRegex = "^.{8,}$";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; mark the start and end of the string.&lt;/li&gt;
&lt;li&gt;.{8,} ensures the password has at least 8 characters of any type.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this rule prevents extremely short passwords, it does not enforce complexity.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;2. Intermediate Password Validation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To increase security, we can require passwords to contain at least one letter and one digit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string intermediateRegex = "^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(?=.*[A-Za-z])&lt;/code&gt; ensures at least one letter (uppercase or lowercase).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(?=.*\d)&lt;/code&gt; ensures at least one digit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[A-Za-z\d]{8,}&lt;/code&gt; allows only letters and digits, with a minimum of 8 characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This regex prevents purely numeric or alphabetical passwords but still lacks special character enforcement.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;3. Advanced Password Validation (Strong Passwords)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A strong password should meet the following criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At least 8 characters long.&lt;/li&gt;
&lt;li&gt;Contains at least one lowercase letter.&lt;/li&gt;
&lt;li&gt;Contains at least one uppercase letter.&lt;/li&gt;
&lt;li&gt;Contains at least one digit.&lt;/li&gt;
&lt;li&gt;Contains at least one special character from a predefined set (e.g., @$!%*?&amp;amp;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regex for Strong Passwords:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string advancedRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&amp;amp;])[A-Za-z\d@$!%*?&amp;amp;]{8,}$";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string extendedAdvancedRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&amp;amp;^#()[\]{}|\\/\-+_.:;=,~`])[^\s&amp;lt;&amp;gt;]{8,}$";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(?=.*[a-z])&lt;/code&gt; ensures at least one lowercase letter.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(?=.*[A-Z])&lt;/code&gt; ensures at least one uppercase letter.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(?=.*\d)&lt;/code&gt; ensures at least one digit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(?=.*[@$!%*?&amp;amp;])&lt;/code&gt; ensures at least one special character.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(?=.*[@$!%*?&amp;amp;^#()[\]{}|\\/\-+_.:;=,~])&lt;/code&gt; ensures at least one special character from an extended set.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[^\s&amp;lt;&amp;gt;]{8,}&lt;/code&gt; ensures a minimum length of 8 characters while restricting whitespace and certain unsafe characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern significantly enhances password security and meets most modern security standards.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;4. Implementing Password Validation in C#&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Below is a simple C# program that validates passwords using Regex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Text.RegularExpressions;

public class PasswordValidator
{
    public static bool IsValidPassword(string password, string pattern)
    {
        return Regex.IsMatch(password, pattern);
    }

    public static void Main()
    {
        string password = "StrongP@ss1";

        // Use one of the regex patterns defined above
        string pattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&amp;amp;^#()[\]{}|\\/\-+_.:;=,~`])[^\s&amp;lt;&amp;gt;]{8,}$";

        if (IsValidPassword(password, pattern))
            Console.WriteLine("Password is valid!");
        else
            Console.WriteLine("Password does not meet the requirements.");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Implementing password validation using Regex in C# ensures strong security policies for user authentication. By progressively enforcing length, character variety, and complexity, we can significantly reduce vulnerabilities related to weak passwords.&lt;/p&gt;

&lt;p&gt;For enterprise-level applications, additional security measures such as multi-factor authentication (MFA) and rate-limiting login attempts should also be implemented alongside Regex-based password validation.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Connect with Me!&lt;/strong&gt;&lt;br&gt;
Thanks for reading! If you found this helpful, consider following me for more content like this.&lt;/p&gt;

&lt;p&gt;🌟 &lt;a href="https://www.linkedin.com/in/ravi-vishwakarma-a20b451b6/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;br&gt;
💻 &lt;a href="https://github.com/ravivish786" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your support motivates me to keep sharing valuable insights. Stay tuned for more! 🚀&lt;/p&gt;

&lt;p&gt;Thanks for Reading.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>AddMVC() vs AddControllersWithViews() vs AddControllers() vs AddRazorPages() | ASP.Net Core</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Tue, 28 Jan 2025 02:42:18 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/addmvc-vs-addcontrollerswithviews-vs-addcontrollers-vs-addrazorpages-aspnet-core-bgo</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/addmvc-vs-addcontrollerswithviews-vs-addcontrollers-vs-addrazorpages-aspnet-core-bgo</guid>
      <description>&lt;p&gt;In ASP.NET Core, the methods &lt;code&gt;AddMvc()&lt;/code&gt;, &lt;code&gt;AddControllersWithViews()&lt;/code&gt;, &lt;code&gt;AddControllers()&lt;/code&gt;, and &lt;code&gt;AddRazorPages()&lt;/code&gt; are used to configure services for various application architectures. Each of these methods serves a specific purpose depending on the type of application you are building.&lt;/p&gt;




&lt;h3&gt;
  
  
  AddMvc()
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Configures the app to use the complete ASP.NET Core MVC framework, including support for controllers, views, Razor Pages, and API features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status&lt;/strong&gt;: This method is obsolete in recent ASP.NET Core versions (3.0 and later). It's replaced by more granular methods like AddControllersWithViews() or AddRazorPages().&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: Used in older versions (before ASP.NET Core 3.0) to set up both MVC and Razor Pages in a single step.&lt;/p&gt;




&lt;h3&gt;
  
  
  AddControllersWithViews()
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Configures the app to support controllers and views (for MVC-style web apps). Does not include Razor Pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: Ideal for traditional MVC web applications where controllers and views are the primary focus.&lt;br&gt;
Suitable for server-rendered HTML with routing, model binding, and validation.&lt;br&gt;
&lt;strong&gt;Features&lt;/strong&gt;: Adds support for controllers and views.&lt;br&gt;
Does not support Razor Pages (e.g., .cshtml files under the /Pages folder).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Services.AddControllersWithViews();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  AddControllers()
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Configures the app to support controllers only, for building API applications without views.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: Suitable for building Web APIs. Does not include Razor Pages or support for views.&lt;br&gt;
&lt;strong&gt;Features&lt;/strong&gt;: Adds support for controllers, but excludes Razor Pages and views. Focused on API endpoints, ideal for RESTful services.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Services.AddControllers();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  AddRazorPages()
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Configures the app to support Razor Pages, a page-focused programming model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: Ideal for applications primarily using Razor Pages for server-side rendering. Suitable for smaller apps or apps with a page-oriented architecture (e.g., intranet applications).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Features&lt;/strong&gt;:&lt;br&gt;
Supports Razor Pages (e.g., .cshtml files under the /Pages folder).&lt;br&gt;
Does not support controllers or views unless explicitly added.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Services.AddRazorPages();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  .NET Core Middleware Comparison Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Controllers&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Views&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Razor Pages&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;APIs (JSON/XML responses)&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AddMvc()&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AddControllersWithViews()&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AddControllers()&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AddRazorPages()&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Choosing the Right Method
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;For APIs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;AddControllers()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Example: Building a RESTful API that does not require views or Razor Pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Traditional MVC Applications:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;AddControllersWithViews()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Example: Building a web app with controllers and views for server-side rendering.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Razor Pages Applications:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;AddRazorPages()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Example: Building a simple page-oriented application with &lt;code&gt;.cshtml&lt;/code&gt; files under &lt;code&gt;/Pages&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Hybrid Scenarios:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Combine &lt;code&gt;AddControllersWithViews()&lt;/code&gt; and &lt;code&gt;AddRazorPages()&lt;/code&gt; for apps that need both controllers and Razor Pages.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use specific configurations (e.g., &lt;code&gt;AddControllersWithViews()&lt;/code&gt;, &lt;code&gt;AddRazorPages()&lt;/code&gt;) instead of &lt;code&gt;AddMvc()&lt;/code&gt; for better clarity and performance.&lt;/li&gt;
&lt;li&gt;Choose the appropriate method based on your application's architecture and the required features.&lt;/li&gt;
&lt;li&gt;If your app includes API endpoints and views, combine &lt;code&gt;AddControllersWithViews()&lt;/code&gt; with features like &lt;code&gt;JsonOptions&lt;/code&gt; to configure API behavior.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Connect with Me!&lt;/strong&gt;&lt;br&gt;
Thanks for reading! If you found this helpful, consider following me for more content like this.&lt;/p&gt;

&lt;p&gt;🌟 &lt;a href="https://www.linkedin.com/in/ravi-vishwakarma-a20b451b6/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;br&gt;
💻 &lt;a href="https://github.com/ravivish786" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your support motivates me to keep sharing valuable insights. Stay tuned for more! 🚀&lt;/p&gt;

&lt;p&gt;Thanks for Reading.&lt;/p&gt;

</description>
      <category>netcore</category>
      <category>dotnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Comprehensive Guide to Documentation Comments in C#</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Sun, 19 Jan 2025 11:20:09 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/comprehensive-guide-to-documentation-comments-in-c-379d</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/comprehensive-guide-to-documentation-comments-in-c-379d</guid>
      <description>&lt;p&gt;Documentation comments in C# are structured comments that describe code functionality and are processed by tools like Visual Studio and Sandcastle to generate user-friendly documentation. These comments are written in XML format and placed above code elements like classes, methods, properties, and fields.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Syntax for Documentation Comments in C#&lt;/strong&gt;&lt;br&gt;
In C#, documentation comments start with three slashes (&lt;code&gt;///&lt;/code&gt;) and support XML-based tags to provide detailed descriptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Represents a basic calculator for arithmetic operations.
/// &amp;lt;/summary&amp;gt;
public class Calculator
{
    // Class implementation
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common XML Tags in C#&lt;/strong&gt;&lt;br&gt;
Here are the most commonly used XML tags in C# documentation comments:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;General Tags&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;:   Provides a brief summary of a class, method, or property.&lt;br&gt;
&lt;code&gt;&amp;lt;remarks&amp;gt;&lt;/code&gt;:   Adds additional or detailed remarks about the element.&lt;br&gt;
&lt;code&gt;&amp;lt;example&amp;gt;&lt;/code&gt;:   Contains code examples to demonstrate usage.&lt;br&gt;
&lt;code&gt;&amp;lt;value&amp;gt;&lt;/code&gt;: Describes the value returned by a property.&lt;br&gt;
&lt;code&gt;&amp;lt;inheritdoc&amp;gt;&lt;/code&gt;:    Inherits documentation from the base class or interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method-Specific Tags&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;param&amp;gt;&lt;/code&gt;: Describes a method parameter.&lt;br&gt;
&lt;code&gt;&amp;lt;returns&amp;gt;&lt;/code&gt;:   Documents the return value of a method.&lt;br&gt;
&lt;code&gt;&amp;lt;exception&amp;gt;&lt;/code&gt;: Describes exceptions thrown by the method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-Referencing Tags&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;see&amp;gt;&lt;/code&gt;:   Links to another code element for inline reference.&lt;br&gt;
&lt;code&gt;&amp;lt;seealso&amp;gt;&lt;/code&gt;:   Refers to related code elements for more information.&lt;br&gt;
&lt;code&gt;&amp;lt;c&amp;gt;&lt;/code&gt;: Embeds inline code.&lt;br&gt;
&lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt;:  Displays a block of code in the documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Examples of Documentation Comments in C#&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documenting a Class&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Performs basic arithmetic operations such as addition and subtraction.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;remarks&amp;gt;
/// This class is intended for demonstration purposes only.
/// &amp;lt;/remarks&amp;gt;
public class Calculator
{
    // Class implementation
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Documenting a Method&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Adds two integers and returns the result.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name="a"&amp;gt;The first integer.&amp;lt;/param&amp;gt;
/// &amp;lt;param name="b"&amp;gt;The second integer.&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;The sum of the two integers.&amp;lt;/returns&amp;gt;
/// &amp;lt;exception cref="ArgumentNullException"&amp;gt;Thrown if a or b is null.&amp;lt;/exception&amp;gt;
public int Add(int a, int b)
{
    if (a == null || b == null)
    {
        throw new ArgumentNullException("Parameters cannot be null.");
    }
    return a + b;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Documenting a Property&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Gets or sets the name of the calculator's manufacturer.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;value&amp;gt;
/// A string representing the manufacturer's name.
/// &amp;lt;/value&amp;gt;
public string Manufacturer { get; set; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Documenting an Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// &amp;lt;example&amp;gt;
/// &amp;lt;code&amp;gt;
/// Calculator calc = new Calculator();
/// int result = calc.Add(5, 10);
/// Console.WriteLine(result); // Output: 15
/// &amp;lt;/code&amp;gt;
/// &amp;lt;/example&amp;gt;
public int Add(int a, int b)
{
    return a + b;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best Practices for Documentation Comments in C#&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write Clear Summaries&lt;/strong&gt;: Use  to describe the purpose of a code element in a concise and meaningful way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use  and  Tags Appropriately&lt;/strong&gt;: Ensure all method parameters and return values are documented.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provide Examples&lt;/strong&gt;: Use  tags to demonstrate usage, especially for complex methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link to Related Elements&lt;/strong&gt;: Utilize  and  to refer readers to related parts of the codebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintain Consistency&lt;/strong&gt;: Follow consistent formatting and style for all documentation comments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update Regularly&lt;/strong&gt;: Sync documentation comments with code changes to prevent inaccuracies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more go to Microsoft &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>aspdotnet</category>
      <category>netcore</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Global Class Validator for Enhanced Object Validation in EMS</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Sun, 19 Jan 2025 10:01:25 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/global-class-validator-for-enhanced-object-validation-in-ems-5hjh</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/global-class-validator-for-enhanced-object-validation-in-ems-5hjh</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introducing Global Class Validator: Streamlined Object Validation for Employee Management Systems&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Validation is a critical aspect of any application that handles structured data. To ensure accuracy, consistency, and compliance with business logic, validation must be seamless and reliable.&lt;/p&gt;

&lt;p&gt;Our Global Class Validator is a powerful utility tailored for .NET applications, particularly useful in systems like the Employee Management System. It simplifies object validation by leveraging .NET’s built-in &lt;code&gt;ValidationAttribute&lt;/code&gt;and ensures that your data meets defined standards effortlessly.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Features
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Thread-Safe Error Tracking&lt;/strong&gt;: Errors are captured and stored in a thread-safe manner, ensuring accurate results even in multi-threaded environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Property Validation&lt;/strong&gt;: The validator dynamically inspects all public properties of an object and applies the associated &lt;code&gt;ValidationAttribute&lt;/code&gt;to validate their values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive Error Handling&lt;/strong&gt;: The tool provides methods to retrieve errors in multiple formats:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GetErrors&lt;/code&gt;: Returns a collection of validation errors for detailed inspection.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GetErrorsString&lt;/code&gt;: Provides a string-formatted summary of all validation errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4.&lt;strong&gt;Ease of Use&lt;/strong&gt;: Add a simple &lt;code&gt;IsValid&lt;/code&gt;method to your model, and the validator will handle everything from validation checks to error aggregation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample Usage
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;

namespace Employee_Management_System
{
    internal class EmployeeModel
    {
        public EmployeeModel() { }

        [Required]
        [Range(0, uint.MaxValue)]
        public int EmployeeID { get; set; }

        [Required]
        [StringLength(100, MinimumLength = 2)]
        public string Name { get; set; }

        [Required]
        public string Designation { get; set; }

        [Required]
        public string Department { get; set; }

        [Required]
        [RegularExpression(@"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$", ErrorMessage = "Please enter valid EmailID. ")]
        public string Email { get; set; }

        [Required]
        [RegularExpression(@"^\d{8,}$", ErrorMessage = "Please enter valid PhoneNumber. ")]
        public string Phone { get; set; }

        [Required]
        public DateTime JoiningDate { get; set; }

        public EmployeeModel(int employeeID, string name, string designation, string department, string email, string phone, DateTime joiningDate)
        {
            EmployeeID = employeeID;
            Name = name;
            Designation = designation;
            Department = department;
            Email = email;
            Phone = phone;
            JoiningDate = joiningDate;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s how you can integrate and use the Global Class Validator in your .NET projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var employee = new EmployeeModel { Name = "", Email = "develop" }; // Example model with errors  

bool IsValid = ClassModelValidator&amp;lt;EmployeeModel&amp;gt;.IsValid(employee);

if (!IsValid)
{
     MessageBox.Show( ClassModelValidator&amp;lt;EmployeeModel&amp;gt;.GetErrorsString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Code Implementation
&lt;/h4&gt;

&lt;p&gt;Below is the full implementation of the &lt;code&gt;GlobalClassValidator&lt;/code&gt; class for your reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;

namespace Employee_Management_System.Helper
{
    public static class ClassModelValidator&amp;lt;T&amp;gt; where T : class
    {
        // Errors list for tracking validation issues
        [ThreadStatic]
        private static List&amp;lt;string&amp;gt; Errors;

        static ClassModelValidator()
        {
            Errors = new List&amp;lt;string&amp;gt;();
        }



        public static bool IsValid(T model)
        {
            if (model == null)
            {
                AddError("Model cannot be null.");
                return false;
            }

            Errors.Clear();

            foreach (var property in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                var value = property.GetValue(model);
                foreach (var attribute in property.GetCustomAttributes&amp;lt;ValidationAttribute&amp;gt;())
                {
                    if (!attribute.IsValid(value))
                    {
                        AddError(attribute.FormatErrorMessage(property.Name));
                    }
                }
            }

            return Errors.Count == 0;
        }

        public static IEnumerable&amp;lt;string&amp;gt; GetErrors()
        {
            return Errors;
        }

        public static string GetErrorsString()
        {
            return string.Join(Environment.NewLine, Errors);
        }

        private static void AddError(string error)
        {
            if (!string.IsNullOrEmpty(error))
            {
                Errors.Add(error);
            }
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Benefits of Using Global Class Validator
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Enhances data integrity with minimal overhead.&lt;/li&gt;
&lt;li&gt;Reduces boilerplate code by utilizing reflection and attributes.&lt;/li&gt;
&lt;li&gt;Easily extensible for specific validation requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the Global Class Validator, you can ensure that your Employee Management System is robust, efficient, and error-free.&lt;/p&gt;

&lt;p&gt;Feel free to integrate this utility into your projects and let us know how it transforms your validation processes!&lt;/p&gt;

&lt;p&gt;Thanks for Reading!&lt;/p&gt;

&lt;p&gt;For more details, visit:&lt;br&gt;
🔗 &lt;a href="https://github.com/ravivish786/Employee-Management-System-Unicode" rel="noopener noreferrer"&gt;Employee Management System on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Direct link to the code:&lt;br&gt;
🔗 &lt;a href="https://github.com/ravivish786/Employee-Management-System-Unicode/blob/master/Employee-Management-System/Helper/ClassModelValidator.cs" rel="noopener noreferrer"&gt;ClassModelValidator.cs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to explore the repository and contribute! 🚀&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>aspdotnet</category>
      <category>core</category>
    </item>
    <item>
      <title>Generate SEO-Friendly Slugs from Titles in ASP.NET Core</title>
      <dc:creator>Ravi Vishwakarma</dc:creator>
      <pubDate>Sun, 05 Jan 2025 09:50:55 +0000</pubDate>
      <link>https://dev.to/ravi-vishwakarma-hash/generate-seo-friendly-slugs-from-titles-in-aspnet-core-47o9</link>
      <guid>https://dev.to/ravi-vishwakarma-hash/generate-seo-friendly-slugs-from-titles-in-aspnet-core-47o9</guid>
      <description>&lt;p&gt;To create shorter, user-friendly, or "slugged" URLs with Latin-based names, you can use URL slugging. This process typically involves converting strings (like page titles or names) into lowercase, replacing spaces and special characters with hyphens or other valid URL characters, and ensuring the resulting URL is unique and human-readable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Generating Latin Name Slugs for URLs in ASP.NET Core
&lt;/h2&gt;




&lt;h3&gt;
  
  
  Create a Utility Method for Slug Generation
&lt;/h3&gt;

&lt;p&gt;A utility method can convert any string into a slug-friendly format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Text;
using System.Text.RegularExpressions;
public static class UrlSlugger
{
    public static string GenerateSlug(string input)
    {
        if (string.IsNullOrEmpty(input))
            return string.Empty;

        // Convert to lowercase
        input = input.ToLowerInvariant();

        // Remove diacritics (accents) from Latin characters
        input = RemoveDiacritics(input);

        // Replace spaces and invalid characters with hyphens
        input = Regex.Replace(input, @"[^a-z0-9\s-]", ""); // Allow only alphanumeric, spaces, and hyphens
        input = Regex.Replace(input, @"\s+", "-").Trim();  // Replace spaces with hyphens
        input = Regex.Replace(input, @"-+", "-");         // Replace multiple hyphens with a single one

        return input;
    }

    private static string RemoveDiacritics(string text)
    {
        var normalizedString = text.Normalize(NormalizationForm.FormD);
        var stringBuilder = new StringBuilder();

        foreach (var c in normalizedString)
        {
            var unicodeCategory = System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c);
            if (unicodeCategory != System.Globalization.UnicodeCategory.NonSpacingMark)
            {
                stringBuilder.Append(c);
            }
        }

        return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Using the Slug Generator in Your Application
&lt;/h3&gt;

&lt;p&gt;You can use this slug generator for URLs when defining routes or generating links dynamically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example in Controller:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public IActionResult GenerateSluggedUrl(string title)
{
    string slug = UrlSlugger.GenerateSlug(title);
    return Ok($"Generated slug: {slug}");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Usage:&lt;/strong&gt;&lt;br&gt;
For a title like "Éxample Title for URL!", the slug will be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;example-title-for-url&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h3&gt;
  
  
  Integrating Slugs in Routing
&lt;/h3&gt;

&lt;p&gt;To use slugs in your routing, you can define a route pattern with a {slug} parameter and parse the slug dynamically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example in Program.cs:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.UseEndpoints(endpoints =&amp;gt;
{
    endpoints.MapControllerRoute(
        name: "sluggedRoute",
        pattern: "page/{slug}",
        defaults: new { controller = "Page", action = "Details" });

    // Default route
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example in PageController:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class PageController : Controller
{
    public IActionResult Details(string slug)
    {
        // Retrieve the page data by the slug
        return View(model: $"Page with slug: {slug}");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Testing Slugged URLs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Example URLs:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;/page/example-title-for-url&lt;/code&gt;&lt;br&gt;
&lt;code&gt;/page/sample-page-title&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
You can use the slug parameter in the controller to load content based on the slug (e.g., querying the database for matching pages).&lt;/p&gt;




&lt;h3&gt;
  
  
  Additional Tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ensure slugs are unique for your content. Add a database field to store slugs and validate uniqueness.&lt;/li&gt;
&lt;li&gt;Store slugs in your database alongside the corresponding entity (e.g., articles, pages, or products).&lt;/li&gt;
&lt;li&gt;Use slugs in links to improve SEO and user experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
