Skip to main content
CodeOath
← 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 to Singleton — 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: an InvalidOperationException, immediately.
  • The captive dependency bug has more than one shape: direct constructor injection and manual IServiceProvider resolution both get caught by ValidateScopes in Development — a held-open IServiceScopeFactory scope gets caught by nothing, in any environment, because no rule was technically broken.
  • ValidateScopes being on by default in Development isn't a guarantee it's protecting production — how the app actually starts matters: dotnet run reads launchSettings.json and defaults to Development; a container running the compiled DLL directly defaults to Production, where the same check is off.
  • A try/catch that 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 unguarded INSERT with 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 of await somewhere 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() plus AllowCredentials() 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.