.NET Core API中何时使用Scoped?何时需注册Scoped服务?
Awesome question—this is a common point of confusion when working with .NET Core DI in Web APIs. Let's unpack this step by step.
Scoped services are instantiated once per incoming HTTP request and disposed automatically when the request completes. Here are the key scenarios where they’re the ideal choice:
- Per-request state containers: Use Scoped for classes that hold data tied directly to a single request, like a custom
RequestTrackingContextthat stores a unique request ID, current user claims, or request-specific metadata. This ensures data never leaks between concurrent requests. - Entity Framework Core
DbContext: This is the most ubiquitous use case.DbContextis inherently stateful (it tracks entity changes), so registering it as Scoped guarantees each request gets its own context—avoiding concurrency bugs and ensuring proper database connection cleanup when the request finishes. - Business services dependent on request-specific data: If your service needs access to request context (e.g., current user ID, custom header values), registering it as Scoped lets you safely inject dependencies like
IHttpContextAccessor(or other Scoped services) and use request-specific data throughout the service’s lifecycle. - Stateful services that can’t be shared: Any service with mutable state that shouldn’t be accessed by multiple threads concurrently belongs in Scoped. Unlike Singletons (shared across all requests), Scoped instances are isolated per request, eliminating race conditions entirely.
You’re correct that AsyncLocal<T> lets Singleton services maintain request-scoped state by leveraging the async execution context. But this doesn’t make Scoped services obsolete—here’s why you’d still choose Scoped over hacking Singletons with AsyncLocal:
- Cleaner separation of concerns: Shoving all request-specific state into a Singleton with
AsyncLocalturns it into a messy "god object" that mixes global and request-level logic. Scoped services are purpose-built for request-bound functionality, keeping your code organized and easy to maintain. - Simpler dependency injection: Scoped services can seamlessly depend on other Scoped or Transient services (e.g., a Scoped
OrderProcessingServiceinjecting a ScopedDbContext). With Singletons, you’d have to use factory patterns or manually resolve request-scoped dependencies, adding unnecessary complexity. - Automatic resource cleanup: The DI container automatically disposes Scoped services when the request ends. For resources like database connections or file handles, this ensures proper cleanup without you having to manage it manually (unlike Singletons, where you’d need to handle
AsyncLocalcleanup yourself to avoid memory leaks). - Balanced performance: Scoped services strike a middle ground between Transient (instantiated every time they’re injected) and Singleton (one instance forever). If a service has expensive initialization but only needs to exist per request, Scoped lets you reuse the instance throughout the request lifecycle, saving overhead.
Quick Recap
AsyncLocal is a niche tool for adding request isolation to Singletons when you have to use a Singleton. For most request-bound functionality, Scoped services are the more idiomatic, maintainable choice in .NET Core Web APIs.
内容的提问来源于stack exchange,提问作者micah

