← All posts
.NET Core / Web API71 min total · 19 parts
Building REST APIs with ASP.NET Core: Routing, Middleware, and Dependency Injection
Part 19 of 19 · ~2 min
Common Mistakes Worth Remembering
- Pinning a
DbContext-backed service toSingleton— outright, or by accident via a scope that never gets disposed — falls apart the instant two requests reach it at the same time, and EF Core is not quiet about it: anInvalidOperationException, immediately. - The captive dependency bug has more than one shape: direct constructor injection and manual
IServiceProviderresolution both get caught byValidateScopesin Development — a held-openIServiceScopeFactoryscope gets caught by nothing, in any environment, because no rule was technically broken. ValidateScopesbeing on by default in Development isn't a guarantee it's protecting production — how the app actually starts matters:dotnet runreadslaunchSettings.jsonand defaults to Development; a container running the compiled DLL directly defaults to Production, where the same check is off.- A
try/catchthat fails open ("if the fast check breaks, just let it through") is only as safe as whatever it's falling back on — Bench's fallback was an unguardedINSERTwith no database-level constraint behind it at all. - Getting the pipeline's order wrong — checking permissions before anyone's been identified, or registering CORS too late for it to apply to a request that needed it.
- Reaching for
.Result/.Wait()instead ofawaitsomewhere in the call chain, which quietly drains the shared thread pool under real traffic whether or not it ever escalates all the way to a deadlock. - Letting a raw exception reach the client instead of shaping it into
ProblemDetails— it leaks whatever the exception happened to say, and hands the caller nothing it can actually branch on. - Assuming
AllowAnyOrigin()plusAllowCredentials()will just be a slightly-too-permissive policy — ASP.NET Core's CORS middleware refuses to build that policy at all, which, as these bugs go, is one of the kinder ways to find out you got it wrong. - Trying to hang two complex objects off
[FromBody]on a single action —[ApiController]caps it at one, and says so the moment the app starts, not the moment someone calls it.
Bench's pipeline is built on the identical handler-relay pattern Middleware Pipelines Compared traces through Express.js and Django, the JWT checking behind every [Authorize] attribute here is the same machinery unpacked in OAuth and JWT Explained, and the reason a DbContext can't be shared across threads reaches back to the same value-type/reference-type ground covered in C# Fundamentals. Go work through the DI-lifetime and API-design reasoning from this reference hands-on in the code lab.
Continue learning
- Interview & Career PrepThe Non-Technical Half of the Interview: Behavioral Questions, the STAR Method, and What Recruiters Are Actually Scoring
- AI & LLM EngineeringAI & LLM Engineering Fundamentals: Prompting, RAG, Embeddings, and Function Calling
- TypeScriptTypeScript Fundamentals: Types, Interfaces, Generics, and Why It Catches Bugs Before Runtime