Calling IOptionsSnapshot in tight loops = performance disasterbecause it reconstructs objects every request. ✔ Fix Use IOptionsMonitor for high-frequency reads. public MyService(IOptionsMonitor<MyConfig> cfg) { _cfg = cfg; } Monitor is cached, Snapshot is per-request.
Category: Asp.Net Core
ASP.NET Core “IHostedService Never Stops” — Fixing Broken Graceful Shutdowns
Many background workers never stop on SIGTERM. ✔ Real Fix Bind cancellation token: public Task StartAsync(CancellationToken ct) { _ = DoWorkAsync(ct); return Task.CompletedTask; } 💡 Super Tip In Program.cs: builder.Services.AddHostedService<MyWorker>(); builder.Services.Configure<HostOptions>(o => { o.ShutdownTimeout = TimeSpan.FromSeconds(20); }); Most devs don’t know this exists → massively improves controlled shutdown.
.NET Core Dependency Injection “Multiple Registrations” Bug
This bug causes random services to switch implementations unexpectedly. ⚠ Problem Registering twice: services.AddSingleton<IMailer, SmtpMailer>(); services.AddSingleton<IMailer, FakeMailer>(); ASP.NET Core uses the last registration → extremely hard-to-debug issues. ✔ Fix Remove duplicates OR name your registrations: services.AddSingleton<IMailer, SmtpMailer>(“smtp”); 💡 Hidden Debug Trick Log all registrations: services.ToList().ForEach(x => Console.WriteLine(x.ServiceType));
Rate Limiting in .NET — Fix ‘Random 429 Errors’ the Right Way
The Trap: Most developers dutifully add a Rate Limiter to their API to prevent abuse. However, they often make a critical mistake: they set a Global Limit. If you set a limit of “1000 requests per minute” globally, a single malicious bot can use up all 1000 requests in 5 seconds. The result? Your legitimate […]
ASP.NET Core “ValidateOnStart” — Stop Production Crashes at Runtime
One of the least known but most powerful features. ✔ Use: builder.Services.AddOptions<MySettings>() .Bind(configuration.GetSection(“MySettings”)) .ValidateDataAnnotations() .ValidateOnStart(); Now, invalid configs fail at startup, not during production traffic. 💡 Saves Hours Prevents: null reference crashes broken API keys missing secrets
.NET Core JSON Serializer Pitfalls — Why Your Properties Don’t Serialize
System.Text.Json is fast……but very strict. Common Pain Points Missing getters Private setters PascalCase vs camelCase mismatch Ignored fields without [JsonInclude] Cycles now break serialization ✔ Life-Saving Fix Add: options.PropertyNameCaseInsensitive = true; options.ReferenceHandler = ReferenceHandler.IgnoreCycles; ✔ Hidden Trick For private setters: [JsonInclude] public string Name { get; private set; } This is rarely mentioned but solves […]
ASP.NET Core Middleware Order — Why Your App Breaks for No Reason
The #1 misunderstood part of ASP.NET Core is middleware order. ❌ Bad Order Example app.UseAuthentication(); app.UseRouting(); app.UseAuthorization(); Authentication runs BEFORE routing → user can’t be authorized → random 401s. ✔ Correct Order app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(…); 💡 Life-Saving Insight Routing must ALWAYS come first.Authorization must ALWAYS come after Authentication. Confusing ordering makes APIs randomly break.
Dependency Injection Performance — “AddTransient Everywhere” Is Not the Answer
Many APIs slow down because lifetime choices are wrong. ❌ Common Mistake Using Transient for heavy services: services.AddTransient<EmailService>(); ✔ Correct Use Singleton for stateless services: services.AddSingleton<IEmailSender, EmailService>(); 💡 Why? Fewer allocations Less GC pressure Faster resolving More consistent performance
ASP.NET Core Kestrel Limits — Avoid Flooding & Keep APIs Stable
Kestrel has built-in protection against floods, slow clients, and excessive headers. Key Settings in appsettings.json: “Kestrel”: { “Limits”: { “MaxRequestBodySize”: 1048576, “KeepAliveTimeout”: “00:00:30”, “RequestHeadersTimeout”: “00:00:30” } } 💡 Why This Matters Stops slow-loris attacks Reduces memory pressure Prevents API overload Ensures predictable scaling Bonus Enable HTTP/2 for better concurrency.
.NET Core Connection Pooling — The Hidden Performance Giant
Most API latency problems = opening too many SQL connections. 🚀 Fix Use a single pooled client: builder.Services.AddDbContext<AppDb>(options => options.UseSqlServer(connString, o => o.EnableRetryOnFailure())); ✨ Benefits ⚡ Fast connection acquisition 📉 Lower CPU 🧵 Stable high-load behavior 💡 BonusUse Max Pool Size=200 in connection string for heavy systems.
Minimal APIs Filters — Middleware Without the Middleware Cost
Filters allow cross-cutting logic without writing a full middleware. builder.Services.AddEndpointFilter<ValidationFilter>(); ⚡ Why They’re Great ⚙ Less overhead 🎯 Scoped to specific endpoints 🧽 Cleaner architectures 🔍 Perfect for validation, auth, logging
Clean Dependency Injection — Lifetimes That Every Dev Must Master
⏳ The 3 Lifetimes 🔵 Singleton → stateless global services 🟢 Scoped → per-request (web default) 🟠 Transient → lightweight, multi-instance ❌ Absolute Rule Never inject a Scoped service into a Singleton. Bad: builder.Services.AddSingleton<MyService>(); // depends on DbContext ❌ ✔ Correct Use factory: builder.Services.AddSingleton<MyService>(sp => new MyService(sp.GetRequiredService<IDbContextFactory<AppDb>>())); 🧠 Why? Prevents stale data Eliminates threading issues […]
EF Core: ExecuteUpdate & ExecuteDelete — Why You Should Stop Looping
The old way: foreach (var user in users) user.IsActive = false; await ctx.SaveChangesAsync(); The modern way: await ctx.Users.Where(x => x.IsActive) .ExecuteUpdateAsync(set => set.SetProperty(p => p.IsActive, false)); ⚡ Benefits 🚀 Single SQL statement 🧼 No tracking overhead 💾 Saves CPU + DB roundtrips 🧩 Perfect for batch jobs + cron workers
C# Records — Why They Still Beat Classes for Domain Models
Records are still the cleanest way to model immutable domain data. public record Order(int Id, decimal Total, string Status); ❤️ Why Devs Love Records 🔐 Built-in immutability 📝 Value-based equality ✂ Boilerplate goes to zero 🔄 Perfect for event-driven + DDD ⚡ Extra Tip Use with expressions to clone deeply but safely.
.NET 9 Rate Limiting — Easy API Protection
Rate limiting used to be hard… now it’s literally one line. app.UseRateLimiter(new() { GlobalLimiter = PartitionedRateLimiter.CreateChained( PartitionedRateLimiter.CreateFixedWindow(10, TimeSpan.FromSeconds(5)) ) }); 🔒 Why You Need It 🛡 Stops brute-force 📉 Protects API throughput ⚙ Cloud-native resilience 🌍 Zero-config distributed support (Redis optional) 🧠 Bonus Pair with Minimal APIs for super-light API services.
Async Streams in C# — Clean, Fast, Real-Time Data
🌀 Why Async Streams Shine Async Streams (IAsyncEnumerable<T>) are perfect for streaming data without memory blows. await foreach (var log in service.GetLogsAsync()) { Console.WriteLine(log); } ✨ What They Solve 🔁 Handling live logs 📡 Streamed API responses 🧵 Reducing memory spikes 🌊 Backpressure-friendly async flow 💡 Pro Tip Combine with channels for ultra-responsive pipelines.
EF Core 8 ExecuteUpdate – Batch Updates Without Loops
ExecuteUpdate allows batch updates without tracking overhead. await ctx.Users .Where(u => u.IsActive == false) .ExecuteUpdateAsync(p => p.SetProperty(x => x.IsDeleted, true)); It’s fast, clean, and ideal for big-data operations.
Cleaner .NET APIs Using IEndpointFilter for Validation
Endpoint filters reduce validation noise in Minimal APIs. public class ValidateFilter : IEndpointFilter { public Task<object?> InvokeAsync(EndpointFilterInvocationContext ctx, EndpointFilterDelegate next) { var model = ctx.GetArgument(0); Validator.ValidateObject(model, new()); return next(ctx); } } They keep endpoints clean, reusable, and testable.
Build Clean Background Services with IHostedService in .NET
BackgroundService enables clean, scalable background processing. protected override async Task ExecuteAsync(CancellationToken ct) { while (!ct.IsCancellationRequested) { await DoWork(); await Task.Delay(1000, ct); } } It works perfectly in cloud-native environments.
Stop Calling ToList() Too Early – Improve LINQ Performance
Calling ToList() too early forces premature execution. // Bad var items = db.Users.ToList().Where(…); // Good var items = db.Users.Where(…).ToList(); Let the database handle filtering and execution.
Never Write Retry Logic Again – Use Polly in .NET
Polly provides retry, timeout, fallback, and circuit-breaker patterns. var result = await Policy .Handle() .RetryAsync(3) .ExecuteAsync(() => CallApi()); It’s the gold standard for resilience in .NET microservices.
Clean Minimal APIs in .NET – Automatic DTO Binding Explained
Minimal APIs provide automatic DTO binding, eliminating unnecessary boilerplate. app.MapPost(“/login”, (LoginRequest req) => { return Results.Ok($”Welcome {req.Email}”); }); This improves readability, reduces errors, and works perfectly with endpoint filters.
Write Cleaner Business Logic Using C# Switch Expressions
Switch expressions simplify complex branching logic and make code more readable. var price = plan switch { “free” => 0, “pro” => 19, “biz” => 49, _ => throw new Exception(“Unknown plan”) }; They are ideal for pricing models, rules engines, and state transitions.
EF Core Performance Boost: Use AsNoTracking() When Reading Data
For read-heavy queries, AsNoTracking() provides massive performance improvements. var users = await ctx.Users.AsNoTracking().ToListAsync(); It avoids adding entities to the change tracker, improving speed by 30–50%.
Use IAsyncEnumerable for Streaming Data in .NET
IAsyncEnumerable allows true async streaming, reducing memory usage. await foreach (var user in repo.GetAllAsync()) Console.WriteLine(user.Name); Perfect for large datasets and high-throughput APIs.
Why C# Record Types Are Perfect for Modern DTOs
Records offer immutability, value semantics, and minimal syntax. public record UserDto(int Id, string Name, string Email); They are ideal for APIs, events, and data-transfer layers.
Improve .NET Logging with Structured Log Messages
Structured logs allow smarter querying and cleaner search results. logger.LogInformation(“User {UserId} logged in”, id); They improve observability, diagnostics, and production monitoring.
Boost Async Performance in .NET with ConfigureAwait(false)
Using ConfigureAwait(false) can significantly reduce context-switching overhead in non-UI .NET applications. This is essential for APIs, background services, and any high-throughput async pipeline. await http.SendAsync(req).ConfigureAwait(false); It lowers CPU usage, avoids deadlocks, and improves overall async performance.
Lightning-Fast Lookups in .NET Using MemoryCache
MemoryCache provides a simple and fast in-memory caching solution. var item = cache.GetOrCreate(“users”, entry => { entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10); return repo.GetUsers(); }); Ideal for configuration, lookups, and session data.
Every Async Method Should Accept CancellationToken
CancellationToken prevents zombie tasks and ensures graceful shutdown. public Task RunAsync(CancellationToken ct) It is a must-have for cloud-native workloads.



