Connection pool exhaustion with EF Core DbContext connection lifetime
0 reputation · 28 Dec 2025, 20:43 UTC
0 reputation · 28 Dec 2025, 20:43 UTC
Understanding when EF Core acquires and releases a physical database connection relative to DbContext lifetime and per-command execution is needed to reason about intermittent pool exhaustion. The documented interaction between DbContext connection management, SaveChanges and provider pooling is relevant, as is the effect of holding a DbContext instance across asynchronous boundaries.
The Microsoft.EntityFrameworkCore.Database.Connection logging category documents connection acquisition and release events. Provider pools such as Microsoft.Data.SqlClient maintain their own limits, and EF Core command interception can influence how long a connection remains checked out. The precise conditions under which a new connection is opened for each SaveChanges versus reused within a single DbContext lifetime are not fully clear.
Does EF Core guarantee release of the physical connection after each command when the DbContext remains undisposed? How does the CloseConnection option interact with provider pooling across async await points? What observable signals distinguish EF Core connection management from underlying ADO.NET pool exhaustion?
35275 reputation · 29 Dec 2025, 03:53 UTC
EF Core does not guarantee the release of the physical connection after each command if the DbContext remains undisposed. By default, EF Core opens a connection upon the first execution of a command (such as SaveChanges or ToList) and holds that connection open for the remainder of the DbContext lifetime to optimize performance for subsequent operations.
When a DbContext is held across asynchronous boundaries (e.g., long-running await points), the underlying physical connection remains checked out from the ADO.NET provider pool (such as Microsoft.Data.SqlClient). This prevents other requests from utilizing that connection, which can lead to pool exhaustion even if the database itself is not under heavy load.
The CloseConnection behavior (or calling Database.CloseConnection()) forces the physical connection to be returned to the provider pool immediately after a command completes. While this increases the frequency of connection check-outs/check-ins, it significantly reduces the risk of exhaustion in scenarios where DbContext instances have long lifetimes or are used in highly concurrent asynchronous workflows.
To determine if the issue is rooted in EF Core's lifetime management or general provider pool limits, monitor these specific signals:
Microsoft.EntityFrameworkCore.Database.Connection category. A pattern of ConnectionOpened events without corresponding ConnectionClosed events during a single request indicates the DbContext is holding the connection.SqlException with a message stating "Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool". This is distinct from a command timeout, which occurs after a connection is already established.sys.dm_exec_connections or sys.dm_db_connection_stats to see if the number of active sessions matches the number of active DbContext instances.DbContext is registered as Scoped (per-request) rather than Singleton or Transient in long-lived services.DbContext must persist across long async gaps, call context.Database.CloseConnection() manually after the primary database interaction.Max Pool Size (default is usually 100). If the application legitimately requires more concurrent connections than the pool allows, increase this value.Diagnostic Requirement: To refine this recommendation, please provide the specific DbContext service lifetime (e.g., Scoped, Singleton) and the EF Core version currently in use.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.