Redis Connection Pooling and Cache Operations in .NET
This example demonstrates a production-ready Redis integration in C# using connection pooling and generic cache operations. The implementation separates concerns across three core components: connection management, cache abstraction, and application usage.
Application Entry Point
namespace RedisDemo
{
class Program
{
static void Main(string[] args)
{
var student = CacheProvider.Instance.Retrieve<Student>("student:profile");
CacheProvider.Instance.Store<Student>(
"student:profile",
new Student { Id = 101, FullName = "Alex Chen" },
TimeSpan.FromMinutes(30)
);
var updatedStudent = CacheProvider.Instance.Retrieve<Student>("student:profile");
Console.WriteLine($"Retrieved: {updatedStudent?.FullName}");
Console.ReadKey();
}
}
public class Student
{
public int Id { get; set; }
public string FullName { get; set; }
}
}
Connection Pool Manager
The RedisConnectionPool class iintializes and maintains a thread-safe pool of Redis clients using StackExchange.Redis — a modern, high-performance Redis client for .NET.
using StackExchange.Redis;
using System;
using System.Configuration;
namespace RedisDemo
{
public static class RedisConnectionPool
{
private static readonly Lazy<ConnectionMultiplexer> _lazyConnection;
private static readonly string _connectionString;
static RedisConnectionPool()
{
_connectionString = ConfigurationManager.AppSettings["RedisConnectionString"]
?? "127.0.0.1:6379,abortConnect=false,connectTimeout=5000";
_lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
var options = ConfigurationOptions.Parse(_connectionString);
options.ConnectRetry = 3;
options.ReconnectDelay = TimeSpan.FromMilliseconds(500);
return ConnectionMultiplexer.Connect(options);
});
}
public static ConnectionMultiplexer Instance => _lazyConnection.Value;
public static IDatabase GetDatabase(int db = -1) =>
Instance.GetDatabase(db);
}
}
Cache Abstraction Layer
CacheProvider offers a singleton, disposable wrapper around Redis operations with configurable TTL fallbacks and type safety.
using StackExchange.Redis;
using System;
using System.Configuration;
namespace RedisDemo
{
public class CacheProvider : IDisposable
{
private readonly IDatabase _db;
private readonly TimeSpan _defaultExpiry;
private static readonly object _lock = new object();
private static CacheProvider _instance;
public static CacheProvider Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
_instance ??= new CacheProvider();
}
}
return _instance;
}
}
private CacheProvider()
{
_db = RedisConnectionPool.GetDatabase();
var expiryConfig = ConfigurationManager.AppSettings["DefaultCacheExpiryMinutes"];
_defaultExpiry = double.TryParse(expiryConfig, out var minutes)
? TimeSpan.FromMinutes(minutes)
: TimeSpan.FromMinutes(20);
}
public void Store<T>(string key, T value, TimeSpan? expiry = null)
{
var finalExpiry = expiry ?? _defaultExpiry;
var serialized = System.Text.Json.JsonSerializer.Serialize(value);
_db.StringSet(key, serialized, finalExpiry);
}
public T Retrieve<T>(string key)
{
var json = _db.StringGet(key);
return json.IsNullOrEmpty
? default : System.Text.Json.JsonSerializer.Deserialize<T>(json);
}
public bool Evict(string key) => _db.KeyDelete(key);
public void Dispose() => _db.Multiplexer?.Close();
}
}
Configuraton (Web.config or App.config)
<configuration>
<appSettings>
<add key="RedisConnectionString" value="127.0.0.1:6379,abortConnect=false" />
<add key="DefaultCacheExpiryMinutes" value="30" />
</appSettings>
</configuration>
Deployment Notes
- By default, Redis binds only to
127.0.0.1. To allow remote access: - Edit
redis.conf: comment out allbinddirectives (e.g.,# bind 127.0.0.1) - Configure Windows Firewall to allow inbound TCP traffic on port
6379 - For production environments, enable Redis authentication via the
requirepassdirective and udpate the connection string accordingly.