# AddDbContext vs AddDbContextPool vs AddDbContextFactory in ASP.NET Core: Which to Use in 2026?

## Three Ways to Register DbContext — and Why the Choice Matters

Every ASP.NET Core application that uses Entity Framework Core has to answer the same registration question at startup: `AddDbContext`, `AddDbContextPool`, or `AddDbContextFactory`? At first glance, these three methods appear to do the same thing — make a `DbContext` available through dependency injection. In practice, they have different lifetime semantics, different performance characteristics, and different failure modes. Picking the wrong one for your scenario won't always cause an obvious error; it can silently introduce state leakage, unnecessary object allocation, or subtle bugs in background services that surface only under load.

The deeper picture — including how DbContext registration fits into a production EF Core setup alongside repository patterns, `AsNoTracking`, and migration strategies — is covered in [Chapter 3 of the Zero to Production course](https://aspnetcoreapi.codingdroplets.com/), where each piece is wired into a real, running API codebase from the ground up.

[![ASP.NET Core Web API: Zero to Production](https://newsletter.codingdroplets.com/images/aspnet-core-api-course-banner-1.jpg align="center")](https://aspnetcoreapi.codingdroplets.com/)

If you want the full implementation — annotated, production-ready, and tested — including the exact registration patterns used in a real enterprise API, [Patreon](https://www.patreon.com/CodingDroplets) has the complete source code with every trade-off already worked through.

* * *

## What Each Registration Method Actually Does

Before comparing them, it helps to understand what each method does under the hood — not just what Microsoft's documentation says, but what actually happens to `DbContext` instances at runtime.

### AddDbContext

`AddDbContext<TContext>` registers your context with a **scoped** lifetime by default. That means one `DbContext` instance is created per HTTP request (or per DI scope) and disposed when the scope ends. This is the ASP.NET Core project template default — the one you get when you run `dotnet new webapi`.

The key characteristics:

*   A fresh instance is created for every request scope
    
*   Any `OnConfiguring` or constructor-injected services are initialized per instance
    
*   There is no instance reuse between requests
    
*   Stateful configuration (injecting scoped services into the context) is fully supported
    
*   Thread-safe by nature: one context per scope, one scope per request
    

### AddDbContextPool

`AddDbContextPool<TContext>` was introduced in EF Core 2.0 as a performance optimization. Instead of creating and disposing a `DbContext` for every request, it maintains a pool of reusable instances. When a scope ends and its context is "disposed", EF Core resets the context state and returns the instance to the pool. The next request that needs a context gets a pre-warmed instance from the pool rather than constructing a new one.

The key characteristics:

*   Instances are recycled across scopes; construction cost is paid once
    
*   The pool has a configurable size (default: 1,024 instances)
    
*   Because instances are recycled, **your DbContext must be stateless** — no constructor-injected scoped services, no instance-level mutable state
    
*   EF Core resets change tracking, query filters, and transaction state between uses, but it does **not** re-run `OnConfiguring`
    
*   `IDbContextPooledObjectPolicy<T>` is available if you need custom reset logic
    

### AddDbContextFactory

`AddDbContextFactory<TContext>` registers an `IDbContextFactory<TContext>` rather than a `DbContext` directly. The factory is registered as a **singleton** and creates short-lived `DbContext` instances on demand — meaning they are created and disposed inside the consuming code, not managed by the DI scope.

The key characteristics:

*   No scope dependency: the factory is a singleton; callers control the `DbContext` lifetime explicitly
    
*   Perfect for **background services**, Blazor components, and long-lived singleton services that need to perform database work
    
*   Each `CreateDbContext()` call produces a fresh, independent instance
    
*   Combines cleanly with `AddDbContextPool` — if both are registered, the factory draws instances from the pool
    

* * *

## Side-by-Side Comparison

| Dimension | AddDbContext | AddDbContextPool | AddDbContextFactory |
| --- | --- | --- | --- |
| **DI Lifetime** | Scoped | Scoped (pooled) | Singleton (factory) |
| **Instance Reuse** | ❌ One per scope | ✅ Pooled, recycled | Caller-controlled |
| **Stateful Config** | ✅ Supported | ❌ Not supported | ✅ Supported |
| **Scoped Service Injection** | ✅ Fully supported | ❌ Not allowed | ❌ Not recommended |
| **Background Services** | ⚠️ Needs IServiceScopeFactory | ⚠️ Needs IServiceScopeFactory | ✅ Native fit |
| **Blazor Server** | ⚠️ Scope mismatch risk | ⚠️ Scope mismatch risk | ✅ Native fit |
| **Performance** | Baseline | Higher (allocation savings) | Baseline |
| **Complexity** | Low | Medium | Medium |

* * *

## When to Use AddDbContext

`AddDbContext` is the right choice for **most ASP.NET Core Web API and MVC applications**. It is simple, predictable, and fully supports any configuration approach — including injecting scoped services like `ICurrentUserService` or `ITenantProvider` directly into the context constructor.

Use it when:

*   Your application follows a standard request/response model
    
*   You inject scoped services into your `DbContext` (e.g., for row-level security via global query filters)
    
*   You are using the repository pattern and each repository is also scoped
    
*   You have not profiled a performance bottleneck caused by `DbContext` instantiation
    

Do **not** use it as a shortcut inside background services or Blazor Server components. Injecting a scoped `DbContext` into a long-lived singleton service will cause an `ObjectDisposedException` or stale tracking state — often silently.

* * *

## When to Use AddDbContextPool

`AddDbContextPool` is worth enabling for **high-throughput APIs** where profiling shows measurable overhead from repeated `DbContext` instantiation. The performance gain comes from avoiding the cost of constructing a new instance, wiring up internal services, and running `OnConfiguring` for every request.

Use it when:

*   Your application handles a large volume of short-lived, stateless requests
    
*   Your `DbContext` does not need scoped service injection
    
*   You have validated (through benchmarks or profiling) that context allocation is a bottleneck
    
*   Your context configuration is effectively constant across the application lifetime
    

The most common mistake with pooled contexts is attempting to inject scoped services through the constructor. Because pooled instances are recycled, their constructor does not re-run between uses — which means a `ICurrentUserService` injected at construction time would carry the first user's identity into subsequent requests. This is a security-critical bug, not just a correctness issue. If you need per-request context-state customization with a pooled context, use `IDbContextPooledObjectPolicy<T>` to reset that state on each recycle.

* * *

## When to Use AddDbContextFactory

`AddDbContextFactory` is the right choice when the **caller needs to control the** `DbContext` **lifetime** independently of the DI scope. The two primary scenarios are background services and Blazor Server components.

### Background Services

A `BackgroundService` or `IHostedService` is a singleton. Injecting a scoped `DbContext` into a singleton is a classic mistake — the context is shared across multiple asynchronous operations, violating EF Core's thread-safety requirement. The traditional fix is to inject `IServiceScopeFactory` and create a scope per unit of work. With `IDbContextFactory<T>`, you skip the scope ceremony entirely: create a context, do the work, dispose it.

### Blazor Server

Blazor Server components have a lifetime that spans multiple renders, which does not map cleanly to ASP.NET Core's request scope. A scoped `DbContext` outlives its intended lifetime in this model. `IDbContextFactory<T>` gives each render or event handler its own short-lived context without scope coupling.

### Combining with Pooling

`AddDbContextFactory` and `AddDbContextPool` are not mutually exclusive. When both are registered, the factory draws from the pool. This means you get the explicit lifetime control of the factory pattern with the allocation efficiency of pooling — the best of both approaches for high-throughput background processing.

* * *

## Does EF Core DbContext Registration Affect Query Behavior?

Yes — in one important way. The registration method determines how long change tracking persists. With `AddDbContext` (scoped), change tracking accumulates across the entire request, which is intentional for unit-of-work style commits. With `AddDbContextFactory`, each context instance starts with a clean tracker.

For read-heavy endpoints, this distinction matters less because [AsNoTracking](https://codingdroplets.com/ef-core-loading-strategies-aspnet-core-enterprise-decision-guide) bypasses the change tracker entirely regardless of how the context is registered. The registration choice primarily affects write patterns and long-lived scenarios.

* * *

## The Stale DbContext Problem in Background Services

One of the most common production issues with EF Core registration is the stale `DbContext` problem. It manifests when a background service holds a reference to a `DbContext` across multiple units of work. Because the context accumulates tracked entities and query filter state over time, long-running operations gradually degrade: queries slow down, tracked entity counts climb, and memory usage grows.

The correct pattern in a background service — whether using `IServiceScopeFactory` or `IDbContextFactory<T>` — is to treat each unit of work as a short-lived context. Create it, do the work, dispose it. This pairs directly with the recommendations in [EF Core Connection Pool Exhaustion in ASP.NET Core: Root Cause and Fix](https://codingdroplets.com/ef-core-connection-pool-exhaustion-aspnet-core-production-fix) — pooling at the ADO.NET level and pooling at the `DbContext` level are complementary, not competing, strategies.

* * *

## Decision Matrix

| Scenario | Recommended Registration |
| --- | --- |
| Standard Web API / MVC application | `AddDbContext` |
| High-throughput API, stateless context | `AddDbContextPool` |
| Background service / Worker Service | `AddDbContextFactory` |
| Blazor Server component | `AddDbContextFactory` |
| Context needs scoped service injection | `AddDbContext` |
| Row-level security via global query filters | `AddDbContext` |
| High-throughput + explicit lifetime control | `AddDbContextFactory` + `AddDbContextPool` |

* * *

## Anti-Patterns to Avoid

**Injecting a pooled context that holds scoped state.** If you use `AddDbContextPool` and inject anything scoped or request-specific into the context constructor — user identity, tenant resolvers, ambient state — you will get state leakage across requests. This is the most common pooling mistake in production codebases.

**Using AddDbContext in a singleton BackgroundService.** This forces you to resolve the scoped `DbContext` from a singleton, which EF Core will refuse at runtime with `InvalidOperationException: Cannot consume scoped service 'AppDbContext' from singleton`. Use `IServiceScopeFactory` or `IDbContextFactory<T>` instead.

**Never disposing factory-created contexts.** When you call `factory.CreateDbContext()`, you own the context lifetime. If you do not dispose it — via `using` or explicit `Dispose()` — connections remain open indefinitely, and your application will exhaust the underlying connection pool under load.

**Switching from AddDbContext to AddDbContextPool without a stateless audit.** Many codebases have subtle per-request state hidden in a `DbContext` subclass — overridden `SaveChangesAsync` that stamps audit fields using injected services, for example. Switching to pooled registration silently breaks these patterns because the constructor does not re-run.

* * *

## FAQ

### What is the difference between AddDbContext and AddDbContextPool in EF Core?

`AddDbContext` creates a new `DbContext` instance for each request scope and disposes it when the scope ends. `AddDbContextPool` recycles instances from an internal pool, avoiding repeated construction costs. The trade-off is that pooled contexts cannot hold scoped service state — the context must be stateless between uses.

### Does AddDbContextPool improve performance significantly?

For most mid-traffic applications, the difference is negligible. `AddDbContextPool` produces measurable throughput gains at high request volumes — typically hundreds of requests per second per instance — where object allocation and initialization overhead becomes a bottleneck. Always benchmark your specific workload before switching.

### Can I use AddDbContextFactory with AddDbContextPool at the same time?

Yes. When both are registered, the `IDbContextFactory<T>` draws instances from the pool. This lets you combine the explicit lifetime control of the factory pattern with the allocation efficiency of pooling — particularly useful for high-throughput background processors.

### Why can't I inject scoped services into a pooled DbContext?

Because pooled `DbContext` instances are reused across multiple requests. The constructor only runs once — when the instance is first created. Any scoped service injected at construction time retains the state from the first request that created the instance. All subsequent requests share that stale reference, which is incorrect and potentially a serious security issue.

### Should I use AddDbContextFactory for all background services in ASP.NET Core?

For background services that access the database, `AddDbContextFactory` is the cleanest approach. It eliminates scope creation boilerplate (`IServiceScopeFactory`), makes lifetime explicit, and works correctly with both pooling and non-pooling registration. The alternative — creating a `IServiceScope` inside the background service — is valid but adds indirection.

### What happens to tracked entities when a pooled DbContext is returned to the pool?

EF Core automatically resets the change tracker and clears any local entity sets when a pooled context is returned. You do not need to manually call `ChangeTracker.Clear()`. However, state that bypasses EF Core's reset mechanism — such as custom properties on your `DbContext` subclass — will not be cleared unless you implement `IDbContextPooledObjectPolicy<T>`.

### Is DbContext registration relevant for integration tests in ASP.NET Core?

Yes. Integration tests using `WebApplicationFactory` typically replace the production `DbContext` registration with an in-memory or SQLite provider. If the production code uses `AddDbContextPool`, the test replacement must match the expected interface — injecting `TContext` directly will still work, but if production code resolves `IDbContextFactory<T>`, the test setup must also register the factory.
