Skip to main content

Why Each Request Gets Its Own DbContext in ASP.NET Core

· 5 min read
Jagdish Kumawat
Founder @ Dewiride

In ASP.NET Core you ask for a DbContext, and one appears. Which one? Each HTTP request gets its own, shared by everything in that request, and it's disposed of when the request ends. This guide proves that with real IDs, and explains what the rule means for code that lives outside a request.

Prerequisites​

See It​

Every DbContext has an ID. This endpoint asks for the context twice, and returns the start of each ID:

A temporary endpoint
app.MapGet("/demo/context-id-short", (InventoryDbContext first, InventoryDbContext second) =>
Results.Ok(new
{
first = first.ContextId.InstanceId.ToString()[..8],
second = second.ContextId.InstanceId.ToString()[..8],
same = ReferenceEquals(first, second),
}));

Two requests in a row:

Response body (the first request)
{
"first": "39f5e94b",
"second": "39f5e94b",
"same": true
}
Response body (the second request)
{
"first": "d375eb75",
"second": "d375eb75",
"same": true
}

A terminal with two responses: in each one, first and second have the same ID and same is true, and the ID differs between the two requests.

Within a request, the same object, even when asked for twice. Across requests, a different one.

Why It Works This Way​

AddDbContext registers the context with a scoped lifetime, and ASP.NET Core creates one scope per request. EF Core's DbContext lifetime page gives the reasons:

  • A context is for one piece of work. "A DbContext instance is designed to be used for a single unit-of-work", and "In many web applications, each HTTP request corresponds to a single unit-of-work."
  • It isn't safe to share. "DbContext is Not thread-safe. Don't share contexts between threads."
  • The request is a natural boundary. "each request gets a separate dependency injection scope (and therefore a separate DbContext instance)."

So two requests at the same moment can't get in each other's way. Each has its own change tracker, and takes its own connection from the pool.

What This Means in Practice​

SituationWhat happensDo this
Two services in the same requestThey share a context, so they share its changesNothing: it's why SaveChangesAsync in one saves changes made by the other
A Task.Run that outlives the requestCannot access a disposed context instanceCreate a scope in the task
Queries started at the same timeA second operation was startedawait each one
A singleton that needs the databaseCannot consume scoped service from singletonInject IServiceScopeFactory
Startup code in Program.csCannot resolve scoped service from root providerUse app.Services.CreateAsyncScope()

Common Mistakes​

MistakeWhat happensDo this instead
Storing a context in a static fieldAll requests share it, with errors under loadAsk for it per request
Registering it as a singletonThe same, and its change tracker grows foreverKeep the default
Disposing of an injected context yourselfOthers in the request get an errorLet the scope dispose of it
Keeping tracked entities between requestsThey belong to a disposed contextLoad them again

FAQ​

Isn't creating a context for each request slow?​

No. EF Core's Advanced Performance Topics page says: "A DbContext is generally a light object: creating and disposing one doesn't involve a database operation, and most applications can do so without any noticeable impact on performance." For very high traffic, the same page describes context pooling, with AddDbContextPool.

Do connections stay open for the whole request?​

No. The same page says EF Core "generally opens connections just before each operation (e.g. query), and closes it right afterwards, causing it to be returned to the pool".

Conclusion​

AddDbContext gives you one DbContext per HTTP request: the same object everywhere in the request, and a new one for the next. That matches how a context should be used, for one unit of work and one thread at a time. Anything outside a request, such as background tasks, singletons and startup code, needs a scope of its own.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.