What is database connection pooling, and why does it matter?
Quick Answer
Opening a physical database connection (TCP handshake, auth, TLS) is expensive. ADO.NET keeps a pool of open connections keyed by the exact connection string; new SqlConnection(...).Open() borrows one and Dispose() returns it. So you should open connections as late and close them as early as possible — the pool makes that cheap.
Establishing a real connection to SQL Server involves a network round-trip, authentication and often TLS, which is slow. So ADO.NET pools connections: when you open one it hands you an idle physical connection from the pool, and disposing it returns it rather than closing it. That's why the guidance is counter-intuitive — open late, dispose immediately, don't hold a connection open for the lifetime of a request. The classic bug is not disposing, usually a missing using; those connections stay checked out until finalization and under load you hit the max pool size and start getting timeout exceptions.
- Always wrap connections in
using— a leaked (undisposed) connection is held out of the pool until GC finalises it, and enough leaks exhaust the pool (Timeout expiredafterMax Pool Size, default 100). - Connection strings must match exactly (including credentials) to share a pool.
- EF Core manages this for you;
DbContextitself is not pooled unless you useAddDbContextPool. - Symptoms of leaks: intermittent pool-timeout exceptions under load that clear after a restart.