Skip to main content

C# Connection Pooling

Database connections are expensive resources. Opening and closing connections repeatedly can significantly impact your application's performance. In this tutorial, you'll learn how C# implements connection pooling to solve this problem, making your database applications faster and more efficient.

What is Connection Pooling?

Connection pooling is a technique that maintains a "pool" of open database connections that can be reused when needed, rather than creating new connections for each database operation. When a client requests a connection, it's taken from the pool (if available) instead of creating a new one, and when the client is done, the connection returns to the pool instead of being closed.

Connection Pooling Diagram

Benefits of Connection Pooling

  • Improved Performance: Reusing connections eliminates the overhead of establishing new connections.
  • Resource Efficiency: Reduces the number of active connections to the database.
  • Scalability: Handles more concurrent users with fewer resources.
  • Connection Management: Automatically handles connection lifecycle.

How Connection Pooling Works in C#

In C#, connection pooling is enabled by default when using ADO.NET to connect to databases. The .NET Framework manages connection pools behind the scenes without requiring explicit coding from developers.

Key Components

  1. Connection String: Contains pooling parameters
  2. Connection Pool Manager: Tracks and manages available connections
  3. Pool Groups: Collections of connections with the same connection string

Basic Connection Pooling Example

Here's a simple example showing how connection pooling works implicitly in C#:

csharp
using System;
using System.Data.SqlClient;
using System.Diagnostics;

class ConnectionPoolingDemo
{
static void Main()
{
string connectionString = "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;";

Console.WriteLine("First connection (pool creation):");
Stopwatch stopwatch = new Stopwatch();

// First connection - pool will be created
stopwatch.Start();
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
Console.WriteLine("Connection opened");
}
stopwatch.Stop();
Console.WriteLine($"Time taken: {stopwatch.ElapsedMilliseconds}ms\n");

// Second connection - will use connection from pool
Console.WriteLine("Second connection (from pool):");
stopwatch.Restart();
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
Console.WriteLine("Connection opened");
}
stopwatch.Stop();
Console.WriteLine($"Time taken: {stopwatch.ElapsedMilliseconds}ms");
}
}

Output:

First connection (pool creation):
Connection opened
Time taken: 823ms

Second connection (from pool):
Connection opened
Time taken: 12ms

Notice how the second connection opens much faster because it reuses a connection from the pool!

Configuring Connection Pooling

You can configure connection pooling by adding parameters to your connection string:

csharp
// Connection string with pooling configuration
string connectionString = "Server=myServerAddress;Database=myDataBase;" +
"User Id=myUsername;Password=myPassword;" +
"Min Pool Size=5;Max Pool Size=100;" +
"Pooling=true;";

Common Pooling Parameters

ParameterDescriptionDefault
PoolingEnables or disables poolingtrue
Min Pool SizeMinimum number of connections maintained in the pool0
Max Pool SizeMaximum number of connections allowed in the pool100
Connection LifetimeMaximum lifetime of a connection in seconds0 (no limit)
Connection ResetControls whether connection state is reset when returned to the pooltrue

Connection Pool Management

Sometimes you may need to control the connection pool more explicitly:

Clearing the Connection Pool

csharp
// Clear all connection pools
SqlConnection.ClearAllPools();

// Clear a specific connection pool
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlConnection.ClearPool(connection);
}

Real-World Example: Web API with Connection Pooling

Here's a practical example showing how connection pooling benefits a web API:

csharp
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly string _connectionString;

public ProductsController(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("DefaultConnection");
}

[HttpGet]
public async Task<IActionResult> GetProducts()
{
var products = new List<Product>();

// Each request gets a connection from the pool
using (var connection = new SqlConnection(_connectionString))
{
await connection.OpenAsync();

using (var command = new SqlCommand("SELECT Id, Name, Price FROM Products", connection))
{
using (var reader = await command.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
products.Add(new Product
{
Id = reader.GetInt32(0),
Name = reader.GetString(1),
Price = reader.GetDecimal(2)
});
}
}
}
} // Connection is returned to the pool here

return Ok(products);
}
}

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}

With connection pooling, this API can handle many concurrent requests efficiently because each request doesn't need to create a new database connection.

Best Practices for Connection Pooling

  1. Use a consistent connection string for similar operations to maximize pool reuse.
  2. Close connections properly using the using statement or explicit Close() calls.
  3. Set appropriate pool sizes based on your application's needs.
  4. Avoid connection string variations (even comments or whitespace differences create separate pools).
  5. Don't disable connection pooling unless you have a very specific reason.
  6. Monitor pool performance in production applications.

Anti-Patterns to Avoid

csharp
// DON'T: Keep connections open for long periods
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
// Long-running process or user interaction here
connection.Close();

// DON'T: Create dynamic connection strings
for (int i = 0; i < 100; i++) {
// This creates 100 different pools!
string connString = $"Server=myServer;Database=myDB;User Id=user{i};Password=pwd;";
using (SqlConnection conn = new SqlConnection(connString))
{
// ...
}
}

Monitoring Connection Pools

You can monitor connection pool performance using:

  1. Performance counters:

    • .NET Data Provider for SqlServer > NumberOfPooledConnections
    • .NET Data Provider for SqlServer > NumberOfActiveConnections
  2. SQL Server Dynamic Management Views:

    • sys.dm_exec_connections
    • sys.dm_exec_sessions
  3. Logging in your application:

csharp
public static void LogConnectionPoolStats()
{
Console.WriteLine($"Current connections: {SqlConnection.ConnectionPool.Count}");
Console.WriteLine($"Available connections: {SqlConnection.ConnectionPool.Available}");
}

Connection Pooling with Different Providers

While we've focused on SQL Server with SqlConnection, other ADO.NET providers also implement connection pooling:

  • MySql: MySqlConnection (MySql.Data)
  • PostgreSQL: NpgsqlConnection (Npgsql)
  • Oracle: OracleConnection (Oracle.ManagedDataAccess)

Each provider may have slightly different connection string parameters and pooling behaviors.

Summary

Connection pooling is a critical feature that significantly improves the performance and scalability of database applications in C#. It works automatically in most cases, but understanding how it works allows you to:

  • Configure it optimally for your application's needs
  • Follow best practices to maximize its benefits
  • Avoid common pitfalls that can reduce its effectiveness

By properly leveraging connection pooling, your applications will handle more concurrent users with better response times and fewer server resources.

Additional Resources

Exercises

  1. Modify the first example to include a loop that opens and closes connections 100 times. Compare the performance with and without connection pooling.
  2. Create a program that demonstrates the impact of setting different Min Pool Size and Max Pool Size values.
  3. Write code that connects to your database with different connection strings and observe how many connection pools are created.
  4. Build a small web application that serves multiple concurrent requests and monitor the connection pool behavior.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)