StackExchange.Redis.Extensions is a library that extends StackExchange.Redis, making it easier to work with Redis in .NET applications. It wraps the base library with serialization, connection pooling, and higher-level APIs so you can store and retrieve complex objects without writing boilerplate code.
AI-Ready: This library provides an
llms.txtfile for AI coding assistants and a Claude Code plugin for configuration, scaffolding, and troubleshooting.claude plugin add imperugo/StackExchange.Redis.ExtensionsThen use
/redis-configure,/redis-scaffold, or/redis-diagnosein Claude Code.
- Store and retrieve complex .NET objects with automatic serialization
- Multiple serialization providers (System.Text.Json, Newtonsoft, Protobuf, MsgPack, MemoryPack, and more)
- Connection pooling with LeastLoaded and RoundRobin strategies
- Pub/Sub messaging with typed handlers
- Hash operations with per-field expiry (Redis 7.4+)
- GeoSpatial indexes (GEOADD, GEOSEARCH, GEODIST, etc.)
- Redis Streams with consumer group support
- Set, List, and Sorted Set operations
- Key tagging and search
- Transparent compression (GZip, Brotli, LZ4, Snappy, Zstandard)
- Azure Managed Identity support
- ASP.NET Core integration with DI
- Multiple named Redis instances
- OpenTelemetry integration
- .NET Standard 2.1, .NET 8, .NET 9, .NET 10
graph TB
App[Your Application] --> DI[ASP.NET Core DI]
DI --> Factory[IRedisClientFactory]
Factory --> Client1[IRedisClient - Default]
Factory --> Client2[IRedisClient - Named]
Client1 --> DB[IRedisDatabase]
DB --> Pool[Connection Pool Manager]
Pool --> C1[Connection 1]
Pool --> C2[Connection 2]
Pool --> C3[Connection N...]
DB --> Ser[ISerializer]
Ser --> Comp[CompressedSerializer?]
Comp --> Inner[System.Text.Json / Newtonsoft / ...]
C1 --> Redis[(Redis Server)]
C2 --> Redis
C3 --> Redis
dotnet add package StackExchange.Redis.Extensions.Core
dotnet add package StackExchange.Redis.Extensions.System.Text.Json
dotnet add package StackExchange.Redis.Extensions.AspNetCore{
"Redis": {
"Password": "",
"AllowAdmin": true,
"Ssl": false,
"ConnectTimeout": 5000,
"SyncTimeout": 5000,
"Database": 0,
"Hosts": [
{ "Host": "localhost", "Port": 6379 }
],
"PoolSize": 5,
"IsDefault": true
}
}var redisConfig = builder.Configuration.GetSection("Redis").Get<RedisConfiguration>();
builder.Services.AddStackExchangeRedisExtensions<SystemTextJsonSerializer>(redisConfig);public class MyService(IRedisDatabase redis)
{
public async Task Example()
{
// Store an object
await redis.AddAsync("user:1", new User { Name = "Ugo", Age = 38 });
// Retrieve it
var user = await redis.GetAsync<User>("user:1");
// Store with expiry
await redis.AddAsync("session:abc", sessionData, TimeSpan.FromMinutes(30));
// Bulk operations
var items = new[]
{
Tuple.Create("key1", "value1"),
Tuple.Create("key2", "value2"),
};
await redis.AddAllAsync(items, TimeSpan.FromHours(1));
// Search keys
var keys = await redis.SearchKeysAsync("user:*");
}
}| Package | Description | NuGet |
|---|---|---|
| Core | Core library with abstractions and implementations | |
| AspNetCore | ASP.NET Core DI integration and middleware |
// Set a hash field
await redis.HashSetAsync("user:1", "name", "Ugo");
await redis.HashSetAsync("user:1", "email", "ugo@example.com");
// Get a hash field
var name = await redis.HashGetAsync<string>("user:1", "name");
// Set with per-field expiry (Redis 7.4+)
await redis.HashSetWithExpiryAsync("user:1", "session", sessionData, TimeSpan.FromMinutes(30));
// Query field TTL
var ttl = await redis.HashFieldGetTimeToLiveAsync("user:1", new[] { "session" });// Add locations
await redis.GeoAddAsync("restaurants", new[]
{
new GeoEntry(13.361389, 38.115556, "Pizzeria Da Michele"),
new GeoEntry(15.087269, 37.502669, "Trattoria del Corso"),
new GeoEntry(12.496366, 41.902782, "Da Enzo al 29"),
});
// Distance between two places
var km = await redis.GeoDistanceAsync("restaurants",
"Pizzeria Da Michele", "Trattoria del Corso", GeoUnit.Kilometers);
// Search within 200km of a point
var nearby = await redis.GeoSearchAsync("restaurants", 13.361389, 38.115556,
new GeoSearchCircle(200, GeoUnit.Kilometers),
count: 10, order: Order.Ascending);// Publish typed events
await redis.StreamAddAsync("orders", "payload", new Order { Id = 1, Total = 99.99m });
// Consumer group workflow
await redis.StreamCreateConsumerGroupAsync("orders", "processors");
var entries = await redis.StreamReadGroupAsync("orders", "processors", "worker-1");
foreach (var entry in entries)
{
// Process the message
await redis.StreamAcknowledgeAsync("orders", "processors", entry.Id!);
}// Subscribe to typed messages
await redis.SubscribeAsync<OrderEvent>("orders:new", async order =>
{
Console.WriteLine($"New order: {order.Id}");
});
// Publish
await redis.PublishAsync("orders:new", new OrderEvent { Id = 42 });// Enable transparent compression with any serializer
services.AddStackExchangeRedisExtensions<SystemTextJsonSerializer>(config);
services.AddRedisCompression<LZ4Compressor>(); // That's it!
// All operations automatically compress/decompress
await redis.AddAsync("large-data", myLargeObject); // stored compressed
var obj = await redis.GetAsync<MyObject>("large-data"); // decompressed automaticallyvar config = new RedisConfiguration { /* ... */ };
config.ConfigurationOptionsAsyncHandler = async opts =>
{
await opts.ConfigureForAzureWithTokenCredentialAsync(new DefaultAzureCredential());
return opts;
};graph LR
subgraph Pool["Connection Pool (PoolSize=5)"]
C1["Connection 1<br/>Outstanding: 3"]
C2["Connection 2<br/>Outstanding: 0"]
C3["Connection 3<br/>Outstanding: 7"]
C4["Connection 4<br/>Outstanding: 1"]
C5["Connection 5<br/>Outstanding: 2"]
end
Op["GetConnection()"] -->|LeastLoaded| C2
Op -->|RoundRobin| C4
style C2 fill:#90EE90
style C4 fill:#87CEEB
The pool automatically skips disconnected connections and falls back gracefully when all connections are down, letting StackExchange.Redis's internal reconnection logic recover.
| Strategy | Behavior |
|---|---|
LeastLoaded (default) |
Picks the connected connection with fewest outstanding commands |
RoundRobin |
Random selection among connected connections |
All values stored in Redis go through the configured ISerializer. This means:
- A
stringvalue"hello"is stored as"\"hello\""(JSON-encoded) - Use
IRedisDatabase.Databasefor raw Redis operations without serialization - All serializers follow the same convention:
nullinput produces an empty byte array
| Property | Default | Description |
|---|---|---|
Hosts |
Required | Redis server endpoints |
Password |
null |
Redis password |
Database |
0 |
Database index |
Ssl |
false |
Enable TLS |
PoolSize |
5 |
Number of connections in the pool |
ConnectionSelectionStrategy |
LeastLoaded |
Pool selection strategy |
SyncTimeout |
5000 |
Sync operation timeout (ms) |
ConnectTimeout |
5000 |
Connection timeout (ms) |
KeyPrefix |
"" |
Prefix for all keys and channels |
AllowAdmin |
false |
Enable admin commands |
ClientName |
null |
Connection client name |
KeepAlive |
-1 |
Heartbeat interval (seconds). -1 = SE.Redis default, 0 = disabled |
ServiceName |
null |
Sentinel service name |
MaxValueLength |
0 |
Max serialized value size (0 = unlimited) |
WorkCount |
CPU*2 |
I/O threads per SocketManager |
ConnectRetry |
null |
Connection retry count |
CertificateValidation |
null |
TLS certificate validation callback |
CertificateSelection |
null |
TLS client certificate selection callback |
ConfigurationOptionsAsyncHandler |
null |
Async callback for custom ConfigurationOptions setup (e.g. Azure) |
Full documentation is available in the doc/ folder:
Getting Started
Configuration
Serializers
Features
- Usage Guide — Add, Get, Replace, Bulk operations
- GeoSpatial Indexes
- VectorSet — AI/ML Similarity Search (Redis 8.0+)
- Redis Streams
- Pub/Sub Messaging
- Hash Field Expiry (Redis 7.4+)
- Compression — GZip, Brotli, LZ4, Snappy, Zstandard
Advanced
- Migration Guide: v11 → v12
- Logging & Diagnostics
- Multiple Redis Servers
- Azure Managed Identity
- OpenTelemetry
- Redis Information Middleware
- NuGet Packages
Thanks to all the people who already contributed!
Please read CONTRIBUTING.md before submitting a pull request. PRs target the master branch only.
StackExchange.Redis.Extensions is Copyright © Ugo Lattanzi and other contributors under the MIT license.