DbContext Pooling in ASP.NET Core: Boost Performance Safely
I once spent the better part of a week chasing a bug that only appeared in our staging environment under heavy load. A user from one organization would suddenly see data belonging to another. We’d check the logs, query the database directly, and everything looked fine. It was a ghost.
The culprit? A single line change someone made to “optimize” our database access: switching AddDbContext
to AddDbContextPool
. That optimization introduced a state-sharing bug that was nearly impossible to reproduce locally.
DbContext
pooling in EF Core is one of those features that promises a big performance win, but it comes with a massive, sneaky gotcha. Let’s break down what it is, where it shines, and how to use it without shooting yourself in the foot.
So, what’s DbContext
pooling anyway?
Normally, when you register your DbContext
in an ASP.NET Core app, you use a scoped lifetime. This is the default and the safest option.
// The standard, safe way: A new instance for every HTTP request.
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
This means for every single web request, the dependency injection container creates a brand-new DbContext
instance. When the request is over, that instance is thrown away. Simple, clean, and isolated.
DbContext
pooling changes the game. Instead of creating a new instance every time, it keeps a “pool” of DbContext
instances ready to go.
// The high-performance way: …