C# const vs readonly

Memory

const → compiler knows the value
readonly → runtime can set it, but cannot reassign it afterward

constreadonly
Java equivalentstatic final (compile-time constant)final
Known atCompile timeRuntime
AssignmentDeclaration onlyDeclaration / constructor
Instance field❌✅
Implicitly static✅❌
Runtime values❌✅
Can reassign later?❌❌
Typical useTrue constantsObject state / DI dependencies
public const int MaxRetries = 3;
 
private readonly IOrderRepository _repository;
 
public OrderService(IOrderRepository repository)
{
    _repository = repository;
}

private vs readonly

private controls who can access the field.
readonly controls whether the field can be reassigned.

private string _name;          // can be changed internally
private readonly string _name; // cannot be reassigned after construction

Java → C#

private final String name;  →  private readonly string _name;
 
static final int MAX = 10;  →  const int Max = 10;
 
static final Guid id =      →  static readonly Guid Id =
    UUID.randomUUID();          Guid.NewGuid();

readonly ≠ deep immutability

private readonly List<string> _items = new();
_items.Add("A"); // ✅
_items = new();  // ❌

readonly prevents reassignment, not mutation of the referenced object.

Cheatsheets

Dot Net

Minimal Api

Minimal Api.Extensions

RequestGroup

Endpoint Filters

  • Enables validation on incoming request

Validation

  • using System.ComponentModel.DataAnnotations
  • [Required][StringLength(50)]
  • [Range(1,100)]
  • Used to work with MVC but needs some enablement for fluent api

Tldr

Transient objects are always different; a new instance is provided to every controller and every service. Scoped objects are the same within a request, but different across different requests. Singleton objects are the same for every object and every request.

Memory trick

T = Tiny/stateless, T t = new(); S = Scope/request, passing a particular object from methodA to methodB S = Shared/application, global


builder.Services.AddTransient<ITransientService, OperationService>();
builder.Services.AddScoped<IScopedService, OperationService>();
builder.Services.AddSingleton<ISingletonService, OperationService>();

1. Transient Lifetime (AddTransient)

  • Behavior:

    • A brand-new instance is created every single time it is requested.
  • Real-World Scenario:

    • A stateless utility or calculation engine, such as a password hasher or an automated text formatting parser.
  • Why Use It:

    • Prevents state leakage. Because the service holds no internal state, giving every consumer a fresh instance completely eliminates multi-threading and concurrency bugs.

Use For Lightweight, stateless services that perform short-lived operations and do not need to share data with other components during an operation.

Eg : calculators, formatters, validators, mappers, and lightweight transformation services.

2. Scoped Lifetime (AddScoped)

  • Behavior:

    • A single instance is created once per client request (one instance per incoming HTTP request in web apps).
  • Real-World Scenario:

    • Entity Framework Core DbContext interacting with a repository pattern.
  • Why Use It:

    • Ensures that everything handling a single HTTP request shares the same Unit of Work without risking cross-request data corruption or tracking conflicts.

Where to Use

Web API controllers, unit-of-work classes, repositories, and business logic layers that interact with databases or maintain state for the duration of a single user transaction.

3. Singleton Lifetime (AddSingleton)

  • Behavior:

    • A single instance is created the first time it is requested and persists across the entire application lifecycle.
  • Real-World Scenario:

    • An in-memory caching service (IMemoryCache) or an application-wide configuration manager.
  • Why Use It:

    • Maximizes performance and reduces memory overhead by preventing repetitive object creation for data that rarely changes and is thread-safe.

Where to Use

Background worker services (IHostedService), caching layers, configuration options, and connection managers shared globally across all users and requests.


Watch out for Captive Dependencies Never inject a Transient or Scoped service directly into a Singleton service. Because the Singleton lives forever, it will "capture" the short-lived dependency, causing it to behave like a Singleton and potentially causing memory leaks or multi-threading bugs.

The rules to remember

DbContext                 → Scoped
 
Request-specific state    → Scoped
 
Stateless lightweight     → Transient
 
Shared immutable state    → Singleton
 
Mutable Singleton         → Check thread safety
 
Singleton → Scoped        → ❌ Avoid
 
ECS Singleton             → Per task/process
 
Lambda Singleton          → Per execution environment

ASP.NET Core Middleware

Summary

Middleware is a component in the ASP.NET Core request pipeline that can inspect, modify, short-circuit, or pass a request to the next component.

Mental Model

HTTP Request
     │
     ▼
┌───────────────┐
│ Middleware A  │
└───────┬───────┘
        ▼
┌───────────────┐
│ Middleware B  │
└───────┬───────┘
        ▼
┌───────────────┐
│ Middleware C  │
└───────┬───────┘
        ▼
    Endpoint
        │
        ▼
     Response

What Middleware Can Do

  • Inspect the request
  • Modify the request
  • Call the next middleware
  • Short-circuit the pipeline
  • Modify the response
  • Handle cross-cutting concerns

Common Middleware

MiddlewarePurpose
Exception handlingCentralized error handling
HTTPS redirectionRedirect HTTP → HTTPS
AuthenticationEstablish user identity
AuthorizationCheck permissions
RoutingSelect endpoint
Static filesServe static content

Interview Trigger

What is middleware in ASP.NET Core?

Short Answer

Middleware is software assembled into the HTTP request pipeline. Each middleware can process the request, invoke the next component, process the response on the way back, or terminate the pipeline early.