Why can't you inject a scoped service into the constructor of custom middleware?
Quick Answer
Conventional middleware is instantiated once at startup (effectively a singleton), so injecting a scoped service like DbContext into its constructor is a captive dependency. Instead, inject scoped services as parameters of the InvokeAsync(HttpContext, IMyScopedService) method — the framework resolves them from the request scope on each call.
Middleware written the conventional way is created once when the app starts and reused for every request — it's basically a singleton. So if I inject a DbContext into its constructor, that's a captive dependency: one context shared across all requests and threads. The fix is to put the scoped dependencies on the InvokeAsync method signature instead — InvokeAsync(HttpContext ctx, AppDbContext db) — and the framework resolves them from the current request's scope each time. Or I implement IMiddleware and register it as scoped, which is the factory-based model where constructor injection works.
- Constructor injection in middleware → only singletons (and
IHttpContextAccessor, transient factories). - Method injection →
public async Task InvokeAsync(HttpContext ctx, AppDbContext db)— resolved per request. - Alternative: implement
IMiddlewareand register it as a scoped/transient service (factory-based middleware) — then constructor injection of scoped services works.