Sunday, April 5, 2026

Dotnet | Request Pipeline

A request in .NET Core (now commonly called ASP.NET Core) flows through a well-defined pipeline. Think of it as a chain of steps that process an HTTP request and produce a response.



Here’s the flow in a clear, conceptual way:


1. Client Sends HTTP Request

A browser, mobile app, or API client sends an HTTP request:

  • URL (e.g., /api/products)
  • Method (GET, POST, etc.)
  • Headers, body

2. Web Server (Kestrel)

The request first hits the built-in web server:

  • Kestrel
  • It listens for incoming HTTP requests and forwards them to the app

Optionally, Kestrel can sit behind:

  • IIS
  • Nginx

3. Middleware Pipeline (Core of the Flow)

This is the most important part.

Middleware are components executed in sequence. Each one can:

  • Handle the request
  • Modify it
  • Pass it to the next middleware

Typical pipeline setup (in Program.cs):

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
Flow inside middleware:
Request → Middleware 1 → Middleware 2 → Middleware 3 → Endpoint
← Response flows back
Common middleware:
  • Exception handling
  • Static files
  • Routing
  • Authentication & Authorization

4. Routing

Routing determines which endpoint should handle the request.

  • Matches URL pattern
  • Maps to a controller/action or minimal API

Example:

[HttpGet("products/{id}")]
public IActionResult GetProduct(int id)

5. Endpoint Execution

Once matched, the request reaches:

Option A: Controllers (MVC/Web API)
  • Controller class handles request
  • Action method executes logic
Option B: Minimal APIs
app.MapGet("/hello", () => "Hello World");

6. Model Binding & Validation

Before the action runs:

  • Data from request (JSON, query, route) is converted into C# objects
  • Validation attributes are applied

7. Business Logic & Services

Inside the action:

  • Calls services (via Dependency Injection)
  • Interacts with database, APIs, etc.

8. Response Creation

The action returns:

  • JSON (Ok(object))
  • View (MVC)
  • Status codes

9. Response Travels Back Through Middleware

The response goes back through the same middleware in reverse order:

  • Logging
  • Error handling
  • Response modification

10. Response Sent to Client

Finally:

  • Kestrel sends the HTTP response back to the client

Quick Summary (Flow)
Client → Kestrel → Middleware → Routing → Endpoint → Business Logic
←──────────── Response (back through middleware) ───────────

Key Concepts to Remember
  • Middleware = pipeline backbone
  • Routing decides where to go
  • Controllers/Endpoints handle logic
  • Dependency Injection is used everywhere

Dotnet | Minimal API

A Minimal API lets you define endpoints directly in Program.cs using simple methods.


Example
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World");

app.MapGet("/products/{id}", (int id) =>
{
return $"Product Id: {id}";
});

app.Run();

What’s happening?
  • MapGet → defines an HTTP GET endpoint
  • No controller, no action methods
  • Everything is defined inline

Key Features

1. Less boilerplate

No need for:

  • Controllers
  • Attributes like [HttpGet]

2. Fast to build
  • Great for small APIs
  • Quick prototypes

3. Built-in dependency injection
app.MapGet("/data", (IMyService service) =>
{
return service.GetData();
});

4. Supports all HTTP methods
app.MapPost("/products", (Product p) => { });
app.MapPut("/products/{id}", (int id) => { });
app.MapDelete("/products/{id}", (int id) => { });

Minimal API vs Controller API

FeatureMinimal APIController API
Code sizeVery smallMore boilerplate
StructureFlatLayered (Controllers)
Best forSmall / simple APIsLarge / enterprise apps
FlexibilityModerateHigh

When to Use Minimal API?

Good for:
  • Microservices
  • Simple CRUD APIs
  • Prototypes
  • Serverless functions

Not ideal for:
  • Large complex applications
  • Apps needing strict architecture (Controllers, Filters, etc.)

Real-World Example
app.MapGet("/users/{id}", async (int id, AppDbContext db) =>
{
var user = await db.Users.FindAsync(id);
return user is not null ? Results.Ok(user) : Results.NotFound();
});

Analogy
  • Minimal API = writing a quick note 
  • Controller API = writing a structured document 

Key Takeaway

Minimal API:

  • Removes ceremony
  • Focuses on simplicity and speed
  • Still uses full power of ASP.NET Core under the hood

Saturday, April 4, 2026

Dotnet | Kestrel

 In ASP.NET Core, Kestrel is the default cross-platform web server used to run your application.


What is Kestrel?

Kestrel is:

  • A lightweight, high-performance web server
  • Built into ASP.NET Core
  • Responsible for handling HTTP requests and responses

Simple Explanation

When a user opens your website:

Browser → Kestrel → ASP.NET Core App → Response → Browser

Kestrel is the engine that listens to requests and sends responses.


Key Features

1. Cross-platform

Runs on:
  • Windows
  • Linux
  • macOS

2. High Performance
  • Built for speed (used in production systems)
  • Handles thousands of requests efficiently

3. Supports HTTP protocols
  • HTTP/1.1
  • HTTP/2
  • HTTP/3 (modern apps)

4. Async & non-blocking
  • Designed for scalable apps

Where is Kestrel Used?

By default in every ASP.NET Core app:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Run();

✔ This automatically starts Kestrel


Kestrel vs IIS

FeatureKestrelIIS (Internet Information Services)
TypeLightweight web serverFull-featured web server
PlatformCross-platformWindows only
UsageDefault in ASP.NET CoreOften used as reverse proxy

Production Setup

Common architecture:

Client → IIS / Nginx → Kestrel → App
  • Nginx or IIS acts as:
    • Reverse proxy
    • Load balancer
  • Kestrel handles app logic

Why not expose Kestrel directly?

Kestrel alone:

  • Doesn’t handle advanced security features
  • No built-in load balancing
  • Limited edge features
So we use:
  • IIS (Windows)
  • Nginx (Linux)

    Load balancing at IIS/Nginx

    Distributing incoming requests across multiple app instances

    Client → Nginx / IIS → App1 (Kestrel)
    → App2 (Kestrel)
    → App3 (Kestrel)

    How Nginx Does It (Simple Example)
    upstream myapp {
    server localhost:5000;
    server localhost:5001;
    }

    server {
    location / {
    proxy_pass http://myapp;
    }
    }

    Nginx or  IIS:

    • Receives request
    • Sends it to one of many Kestrel instances

    Analogy
    • Kestrel = Engine of a car 
    • IIS/Nginx = Driver + security + traffic control 

    Key Takeaway

    Kestrel is:

    • The core web server in ASP.NET Core
    • Fast, lightweight, and cross-platform
    • Usually sits behind a reverse proxy in production

    Dotnet | Exception Handling

    In ASP.NET Core, handling exceptions globally ensures you don’t scatter try-catch blocks everywhere and still return consistent responses.

    There are two main approaches:

    1. Built-in: 
    app.UseExceptionHandler("/error")

    What it does:
    This is a built-in middleware that catches unhandled exceptions and redirects to a specific endpoint.

    Example

    In Program.cs:
    app.UseExceptionHandler("/error");
    Error endpoint:
    [Route("/error")]
    public IActionResult HandleError()
    {
    return Problem("Something went wrong!");
    }

    How it works:
    • Exception occurs anywhere in pipeline
    • Middleware catches it
    • Redirects to /error
    • You return a response (JSON, view, etc.)

    Pros:
    • Very simple to use
    • Built-in and reliable
    • Good for basic/global handling

    Cons:
    • Less control
    • Harder to customize per exception type
    • Redirect-based (less flexible for APIs)

    2. Custom Exception Middleware (Recommended)

    What it is:
    You write your own middleware to handle exceptions exactly how you want.

    Example
    public class ExceptionMiddleware
    {
    private readonly RequestDelegate _next;

    public ExceptionMiddleware(RequestDelegate next)
    {
    _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
    try
    {
    await _next(context);
    }
    catch (Exception ex)
    {
    context.Response.StatusCode = 500;
    context.Response.ContentType = "application/json";

    await context.Response.WriteAsync(
    $"Error: {ex.Message}");
    }
    }
    }
    Register it:
    app.UseMiddleware<ExceptionMiddleware>();

    How it works:
    • Wraps entire request pipeline
    • Catches exceptions
    • Returns custom response directly

    Pros:
    • Full control
    • Can handle different exception types differently
    • Best for APIs (JSON responses)
    • Can add logging, correlation IDs, etc.

    Cons:
    • More code
    • You must maintain it

    Key Differences

    FeatureUseExceptionHandlerCustom Middleware
    SetupVery easyRequires code
    FlexibilityLimitedFull control
    Custom responsesBasicAdvanced
    API-friendlyModerateExcellent
    Exception-specificHardEasy

    Real-World Recommendation

    Use UseExceptionHandler when:
    • Simple app
    • MVC with views
    • Quick setup needed

    Use Custom Middleware when:
    • Building REST APIs
    • Need structured JSON errors
    • Want logging + monitoring

    Best Practice (Common in real apps)

    👉 Combine both:

    if (!app.Environment.IsDevelopment())
    {
    app.UseExceptionHandler("/error");
    }

    app.UseMiddleware<ExceptionMiddleware>();

    Bonus: Handling Specific Exceptions
    catch (KeyNotFoundException ex)
    {
    context.Response.StatusCode = 404;
    }
    catch (UnauthorizedAccessException ex)
    {
    context.Response.StatusCode = 401;
    }

    Simple Analogy
    • UseExceptionHandler = Default customer support desk 
    • Custom middleware = Your own trained support team 

    Final Takeaway

    👉 For Real projects:

    • Mention both
    • Prefer custom middleware for APIs
    • Add logging (Serilog, etc.) in middleware
                                  --------------------------------------------------------------------------------------

    Production-Ready Exception Middleware

    using System.Net;
    using System.Text.Json;

    public class GlobalExceptionMiddleware
    {
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
    {
    _next = next;
    _logger = logger;
    }

    public async Task Invoke(HttpContext context)
    {
    try
    {
    await _next(context);
    }
    catch (Exception ex)
    {
    _logger.LogError(ex, "Unhandled exception occurred");

    await HandleExceptionAsync(context, ex);
    }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception ex)
    {
    var statusCode = ex switch
    {
    KeyNotFoundException => HttpStatusCode.NotFound,
    UnauthorizedAccessException => HttpStatusCode.Unauthorized,
    ArgumentException => HttpStatusCode.BadRequest,
    _ => HttpStatusCode.InternalServerError
    };

    var response = new
    {
    success = false,
    message = ex.Message,
    statusCode = (int)statusCode
    };

    context.Response.ContentType = "application/json";
    context.Response.StatusCode = (int)statusCode;

    var json = JsonSerializer.Serialize(response);

    return context.Response.WriteAsync(json);
    }
    }

    Register Middleware
    app.UseMiddleware<GlobalExceptionMiddleware>();

    Place it early in the pipeline (before other middlewares).


    Sample JSON Response
    {
    "success": false,
    "message": "Product not found",
    "statusCode": 404
    }

    Enhancements (Real-World Level)

    1. Hide sensitive errors in Production
    message = env.IsDevelopment() ? ex.Message : "Something went wrong"

    2. Add Trace ID (VERY IMPORTANT for debugging)
    traceId = context.TraceIdentifier

    3. Use custom exception classes
    public class NotFoundException : Exception
    {
    public NotFoundException(string message) : base(message) { }
    }

    Then:

    NotFoundException => HttpStatusCode.NotFound

    4. Structured Logging (Serilog)

    Works great with:

    • Serilog
    • ELK / Seq dashboards

    Best Practice Architecture

    In real enterprise apps:

    • Controllers → throw exceptions
    • Middleware → handles everything
    • Logging → centralized
    • Response → consistent JSON

    Summarized Paragraph:

    👉 “I use custom global exception middleware in ASP.NET Core to handle exceptions centrally. It logs errors, maps exceptions to proper HTTP status codes, and returns structured JSON responses. For simple apps, UseExceptionHandler can be used, but middleware provides more flexibility.”

    EF | Code Vs Database First

    Code First

    What it is:

    You write C# classes first, and EF generates the database from them.

    Example:
    public class Product
    {
    public int Id { get; set; }
    public string Name { get; set; }
    }

    Then EF creates a table like:
    - Id
    - Name
    Products

    How it works:
    • Define models (classes)
    • Use migrations to create/update DB
    • EF syncs code → database

    Pros:
    • Full control in code
    • Version control friendly (migrations)
    • Great for new projects
    • Clean and flexible

    Cons:
    • Requires migration management
    • Not ideal for complex existing DBs

    Database First

    What it is:
    You design the database first, and EF generates C# classes from it.

    Workflow:
    • Create tables in DB (SQL Server, etc.)
    • Scaffold models using EF
    Scaffold-DbContext "connection_string" Microsoft.EntityFrameworkCore.SqlServer

    How it works:
    • Existing DB → generate models
    • EF reads schema and creates classes

    Pros:
    • Best for existing/legacy databases
    • DB controlled by DBAs
    • No need to design schema in code

    Cons:
    • Harder to maintain changes
    • Regenerating models can overwrite changes
    • Less control compared to Code First

    Key Differences

    FeatureCode FirstDatabase First
    Starting pointC# classesDatabase
    ControlDeveloper (code)Database
    Schema updatesMigrationsDB changes + re-scaffold
    Best forNew projectsExisting databases
    FlexibilityHighModerate

    When to Use What?

    Use Code First:
    • Building a new application
    • Agile development (frequent changes)
    • You want everything in code

    Use Database First:
    • Working with existing DB
    • DB already designed/managed
    • Large enterprise systems

    Simple Analogy
    • Code First = Write blueprint → build house 
    • Database First = House exists → create blueprint 

    Final Takeaway

    Modern development (especially with Entity Framework Core):

    • Mostly prefers Code First + Migrations
    • But Database First is still very useful for real-world legacy systems

    ---------------------------------------------------------------------------------------------------

    How to Update DB When Classes Change?

    When you modify your C# models (add property, remove property, new class, etc.), you don’t directly update the database.

    Instead, you use Migrations.


    Step-by-step

    1. Change your model
    public class Product
    {
    public int Id { get; set; }
    public string Name { get; set; }

    // NEW FIELD
    public decimal Price { get; set; }
    }

    2. Add a Migration

    Add-Migration AddPriceToProduct

    EF generates a migration file like:

    migrationBuilder.AddColumn<decimal>(
    name: "Price",
    table: "Products",
    nullable: false,
    defaultValue: 0m);

    3. Update Database
    Update-Database

    This applies changes to DB.


    What Happens to Existing Data?

    Good news: Data is usually retained

    EF migrations are incremental, not destructive (unless you explicitly make them so).


    Case-by-case behavior

    ➤ Adding a new column
    • Existing data stays
    • New column gets:
      • Default value OR
      • NULL (if nullable)
    public decimal? Price { get; set; } // nullable → safe

    ➤ Adding NON-nullable column
    • Must provide default value OR migration will fail
    public decimal Price { get; set; } // requires default

    ➤ Renaming a column

    EF might:

    • Drop old column ❌
    • Create new column ❌

    👉 This can cause data loss unless handled manually.

    ✔ Fix:

    migrationBuilder.RenameColumn(
    name: "OldName",
    table: "Products",
    newName: "NewName");


    Note: Using rename retain the column data

    ➤  Deleting a property
    • Column is dropped → data lost

    ➤  Adding a new table (new class)
    public class Category
    {
    public int Id { get; set; }
    public string Name { get; set; }
    }

    👉 Migration will:

    • Create new table
    • Existing tables/data remain unchanged

    3. How EF Keeps Track

    EF uses a special table:

    👉 __EFMigrationsHistory

    • Tracks applied migrations
    • Ensures only new changes are applied

    4. Best Practices

    1. Always use migrations

    Never manually change DB in Code First workflow


    2. Make new fields nullable initially
    public string? NewColumn { get; set; }

    Then later enforce required


    3. Review migration code before applying

    Check for:

    • Drops ❌
    • Renames ❌
    • Data loss risks

    4. Use Rename instead of Drop/Create

    Prevents data loss


    5. Backup DB in production

    Always before running:

    Update-Database

    5. Simple Flow Summary
    Change Model → Add-Migration → Review Migration → Update-Database

    Key Takeaway
    • EF does NOT overwrite DB
    • It applies incremental schema changes
    • Existing data is safe unless you explicitly break it

    Node | Cluster Vs Worker Threads

    Cluster: Multiple processes (scale app across CPU cores) Worker Threads: Multiple threads (handle CPU-heavy work inside one process) Cluster...