StackExchange.Redis.Extensions.Core 13.0.1
StackExchange.Redis.Extensions
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.
Features
- 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 with Set Combine (union, intersect, diff)
- HyperLogLog for probabilistic cardinality estimation
- Distributed locking with IAsyncDisposable auto-release
- Bitmap operations for analytics and feature flags
- Lua scripting for atomic server-side operations
- Key tagging and search
- Key management (rename, type, dump/restore)
- Atomic counters (StringIncrement/StringDecrement)
- Transparent compression (GZip, Brotli, LZ4, Snappy, Zstandard)
IDistributedCacheadapter (Microsoft-compatible Hash schema)- ASP.NET Core Health Check for connection pool monitoring
- Azure Managed Identity support
- ASP.NET Core integration with DI and .NET 8+ Keyed Services
- Multiple named Redis instances with
[FromKeyedServices]support - OpenTelemetry integration
- .NET Standard 2.1, .NET 8, .NET 9, .NET 10
Architecture
Quick Start
1. Install packages
dotnet add package StackExchange.Redis.Extensions.Core
dotnet add package StackExchange.Redis.Extensions.System.Text.Json
dotnet add package StackExchange.Redis.Extensions.AspNetCore
2. Configure in appsettings.json
{
"Redis": {
"Password": "",
"AllowAdmin": true,
"Ssl": false,
"ConnectTimeout": 5000,
"SyncTimeout": 5000,
"Database": 0,
"Hosts": [
{ "Host": "localhost", "Port": 6379 }
],
"PoolSize": 5,
"IsDefault": true
}
}
3. Register in DI
var redisConfig = builder.Configuration.GetSection("Redis").Get<RedisConfiguration>();
builder.Services.AddStackExchangeRedisExtensions<SystemTextJsonSerializer>(redisConfig);
4. Use it
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:*");
}
}
NuGet Packages
Core
| Package | Description | NuGet |
|---|---|---|
| Core | Core library with abstractions and implementations | |
| AspNetCore | ASP.NET Core DI integration and middleware |
Serializers (pick one)
Compressors (optional)
Usage Examples
Hash Operations
// 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" });
GeoSpatial
// 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);
Redis Streams
// 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!);
}
Pub/Sub
// 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 });
Atomic Counters
// Increment/decrement with long (default step = 1)
var views = await redis.StringIncrementAsync("page:views");
var stock = await redis.StringDecrementAsync("product:123:stock");
// Custom step
await redis.StringIncrementAsync("stats:bytes", 1024);
// Double precision
await redis.StringIncrementAsync("account:balance", 49.99);
Set Operations
// Combine sets: union, intersect, difference
var allTags = await redis.SetCombineAsync<string>(SetOperation.Union, "user:1:tags", "user:2:tags");
var commonTags = await redis.SetCombineAsync<string>(SetOperation.Intersect, "user:1:tags", "user:2:tags");
// Store result in a new key
await redis.SetCombineAndStoreAsync(SetOperation.Union, "all:tags", new[] { "set:a", "set:b", "set:c" });
HyperLogLog
// Count unique visitors
await redis.HyperLogLogAddAsync("page:home:visitors", userId);
var uniqueCount = await redis.HyperLogLogLengthAsync("page:home:visitors");
// Merge daily counts into a monthly aggregate
await redis.HyperLogLogMergeAsync("visitors:2024:01", new[]
{
"visitors:2024:01:01",
"visitors:2024:01:02",
"visitors:2024:01:03",
});
Distributed Lock
// Acquire a lock with automatic release on dispose
await using var lockObj = await redis.LockAcquireAsync(
"resource:order:123",
expiry: TimeSpan.FromSeconds(30),
maxRetries: 5,
retryDelay: TimeSpan.FromMilliseconds(200));
if (lockObj is not null)
{
// Critical section — lock is held
await ProcessOrder(123);
}
// Lock released automatically on dispose
Bitmap Operations
// Track daily active users (1 bit per user ID)
await redis.StringSetBitAsync("dau:2024-01-15", userId, true);
// Count active users
var activeCount = await redis.StringBitCountAsync("dau:2024-01-15");
// Compute users active on ALL days (AND operation)
await redis.StringBitOperationAsync(Bitwise.And, "wau:all-days", new[]
{
"dau:2024-01-15", "dau:2024-01-16", "dau:2024-01-17",
});
Lua Scripting
// Atomic server-side operations
var script = @"
local current = tonumber(redis.call('GET', KEYS[1]) or '0')
if current < tonumber(ARGV[1]) then
return redis.call('INCR', KEYS[1])
end
return current";
var result = await redis.ScriptEvaluateAsync(
script,
new RedisKey[] { "counter:views" },
new RedisValue[] { 100 });
Key Management
// Rename a key
await redis.KeyRenameAsync("old-key", "new-key");
// Check key type
var type = await redis.KeyTypeAsync("my-key"); // RedisType.String, Set, Hash, ...
// Dump and restore (migrate between databases)
var dump = await redis.KeyDumpAsync("my-key");
await redis.KeyRestoreAsync("my-key-copy", dump, TimeSpan.FromHours(1));
IDistributedCache
// Register the IDistributedCache adapter (call after AddStackExchangeRedisExtensions)
builder.Services.AddStackExchangeRedisExtensions<SystemTextJsonSerializer>(redisConfig);
builder.Services.AddRedisDistributedCache();
// Use standard IDistributedCache anywhere
public class MyService(IDistributedCache cache)
{
public async Task CacheData()
{
await cache.SetAsync("session:abc", data, new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(20),
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(4),
});
var cached = await cache.GetAsync("session:abc");
}
}
Note: Uses a Hash-based schema (
data/absexp/sldexp) compatible withMicrosoft.Extensions.Caching.StackExchangeRedis, enabling zero-downtime migration between providers.
Health Check
// Register the health check
builder.Services.AddHealthChecks()
.AddRedisExtensionsHealthCheck();
// Returns:
// - Healthy: all pool connections active + PING OK
// - Degraded: some connections invalid but Redis responding
// - Unhealthy: all connections invalid or PING fails
Keyed DI Services (.NET 8+)
// Register with named configurations
builder.Services.AddStackExchangeRedisExtensions<SystemTextJsonSerializer>(new[]
{
new RedisConfiguration { Name = "cache", IsDefault = true, /* ... */ },
new RedisConfiguration { Name = "session", /* ... */ },
});
// Inject by name using [FromKeyedServices]
public class MyService(
[FromKeyedServices("cache")] IRedisDatabase cacheDb,
[FromKeyedServices("session")] IRedisDatabase sessionDb)
{
// Each resolves to its own Redis instance with isolated connection pool
}
Compression
// 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 automatically
Azure Managed Identity
var config = new RedisConfiguration { /* ... */ };
config.ConfigurationOptionsAsyncHandler = async opts =>
{
await opts.ConfigureForAzureWithTokenCredentialAsync(new DefaultAzureCredential());
return opts;
};
Connection Pooling
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 |
Serialization Behavior
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
Configuration Reference
| 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) |
Documentation
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
- HyperLogLog — Probabilistic cardinality estimation
- Distributed Lock — Redis-based locking with IAsyncDisposable
- Bitmap Operations — Bit-level analytics and feature flags
- Lua Scripting — Server-side script execution
- Hash Field Expiry (Redis 7.4+)
- Compression — GZip, Brotli, LZ4, Snappy, Zstandard
- Health Check
- IDistributedCache Adapter
Advanced
- Migration Guide: v12 → v13
- Migration Guide: v11 → v12
- Logging & Diagnostics
- Multiple Redis Servers — including Keyed DI Services
- Azure Managed Identity
- OpenTelemetry
- Redis Information Middleware
- NuGet Packages
Contributing
Thanks to all the people who already contributed!
Please read CONTRIBUTING.md before submitting a pull request. PRs target the master branch only.
License
StackExchange.Redis.Extensions is Copyright © Ugo Lattanzi and other contributors under the MIT license.
Showing the top 20 packages that depend on StackExchange.Redis.Extensions.Core.
| Packages | Downloads |
|---|---|
|
StackExchange.Redis.Extensions.LegacyConfiguration
StackExchange.Redis.Extensions.LegacyConfiguration is a library that allows you to use App.Config or WebConfig with StackExchange.Redis.Extensions
|
10 |
|
StackExchange.Redis.Extensions.LegacyConfiguration
StackExchange.Redis.Extensions.LegacyConfiguration is a library that allows you to use App.Config or WebConfig with StackExchange.Redis.Extensions
|
8 |
|
StackExchange.Redis.Extensions.LegacyConfiguration
StackExchange.Redis.Extensions.LegacyConfiguration is a library that allows you to use App.Config or WebConfig with StackExchange.Redis.Extensions
|
7 |
.NET 10.0
- StackExchange.Redis (>= 2.12.14 && < 4.0.0)
.NET 8.0
- StackExchange.Redis (>= 2.12.14 && < 4.0.0)
.NET 9.0
- StackExchange.Redis (>= 2.12.14 && < 4.0.0)
.NET Standard 2.1
- StackExchange.Redis (>= 2.12.14 && < 4.0.0)
- System.Runtime.CompilerServices.Unsafe (>= 6.1.2)
| Version | Downloads | Last updated |
|---|---|---|
| 13.0.1 | 1 | 25.08.2026 |
| 13.0.0 | 1 | 25.08.2026 |
| 12.6.0 | 1 | 25.08.2026 |
| 12.5.0 | 2 | 09.07.2026 |
| 12.2.0 | 4 | 26.05.2026 |
| 12.1.0 | 4 | 28.04.2026 |
| 12.0.0 | 3 | 28.04.2026 |
| 11.0.0 | 9 | 20.02.2025 |
| 10.2.0 | 9 | 20.02.2025 |
| 10.1.0 | 9 | 20.02.2025 |
| 10.0.2 | 9 | 20.02.2025 |
| 10.0.1 | 8 | 20.02.2025 |
| 10.0.0 | 8 | 20.02.2025 |
| 9.1.0 | 8 | 03.06.2025 |
| 9.0.0 | 8 | 03.06.2025 |
| 8.0.5 | 8 | 03.06.2025 |
| 8.0.4 | 8 | 03.06.2025 |
| 8.0.3 | 8 | 03.06.2025 |
| 8.0.2 | 7 | 03.06.2025 |
| 8.0.1 | 7 | 03.06.2025 |
| 8.0.0 | 8 | 03.06.2025 |
| 7.2.1 | 6 | 03.06.2025 |
| 7.1.1 | 7 | 03.06.2025 |
| 7.0.1 | 8 | 03.06.2025 |
| 7.0.0 | 8 | 03.06.2025 |
| 7.0.0-pre | 10 | 20.02.2025 |
| 6.4.5 | 7 | 03.06.2025 |
| 6.4.3 | 7 | 03.06.2025 |
| 6.4.2 | 8 | 03.06.2025 |
| 6.4.1 | 7 | 03.06.2025 |
| 6.4.0 | 7 | 03.06.2025 |
| 6.3.6 | 7 | 03.06.2025 |
| 6.3.5 | 7 | 03.06.2025 |
| 6.3.4 | 7 | 03.06.2025 |
| 6.3.3 | 7 | 03.06.2025 |
| 6.3.2 | 7 | 03.06.2025 |
| 6.3.1 | 8 | 20.02.2025 |
| 6.3.0 | 8 | 03.06.2025 |
| 6.2.2 | 7 | 03.06.2025 |
| 6.2.1 | 8 | 03.06.2025 |
| 6.2.0 | 8 | 03.06.2025 |
| 6.1.7 | 8 | 03.06.2025 |
| 6.1.6 | 7 | 03.06.2025 |
| 6.1.5 | 8 | 03.06.2025 |
| 6.1.1 | 7 | 03.06.2025 |
| 6.1.0 | 8 | 03.06.2025 |
| 6.0.11 | 8 | 20.02.2025 |
| 6.0.10-pre | 9 | 20.02.2025 |
| 6.0.9-pre | 10 | 20.02.2025 |
| 6.0.8-pre | 9 | 20.02.2025 |
| 6.0.7-pre | 10 | 20.02.2025 |
| 6.0.6-pre | 10 | 20.02.2025 |
| 6.0.5-pre | 10 | 20.02.2025 |
| 6.0.4-pre | 9 | 20.02.2025 |
| 6.0.3-pre | 10 | 20.02.2025 |
| 6.0.2-pre | 10 | 20.02.2025 |
| 5.5.0 | 8 | 03.06.2025 |
| 5.4.0 | 11 | 10.02.2025 |
| 5.3.0 | 8 | 03.06.2025 |
| 5.2.0 | 8 | 03.06.2025 |
| 5.1.2 | 7 | 03.06.2025 |
| 5.1.1 | 7 | 03.06.2025 |
| 5.1.0 | 8 | 03.06.2025 |
| 5.0.3 | 8 | 03.06.2025 |
| 5.0.2 | 8 | 03.06.2025 |
| 5.0.1 | 9 | 03.06.2025 |
| 5.0.1-pre | 10 | 20.02.2025 |
| 5.0.0 | 8 | 03.06.2025 |
| 5.0.0-pre | 10 | 20.02.2025 |
| 4.0.5 | 9 | 03.06.2025 |
| 4.0.4 | 9 | 03.06.2025 |
| 4.0.3 | 8 | 03.06.2025 |
| 4.0.2 | 9 | 03.06.2025 |
| 4.0.1 | 8 | 03.06.2025 |
| 4.0.0 | 8 | 03.06.2025 |
| 3.5.0 | 9 | 03.06.2025 |
| 3.4.0 | 7 | 03.06.2025 |
| 3.3.0 | 7 | 03.06.2025 |
| 3.2.0 | 8 | 03.06.2025 |
| 3.1.0 | 9 | 03.06.2025 |
| 3.0.1 | 8 | 03.06.2025 |
| 3.0.0 | 8 | 03.06.2025 |
| 3.0.0-dev | 11 | 20.02.2025 |
| 2.4.0 | 12 | 23.05.2025 |
| 2.3.0 | 8 | 03.06.2025 |
| 2.2.0 | 8 | 03.06.2025 |
| 2.1.0 | 8 | 03.06.2025 |
| 2.0.0 | 8 | 03.06.2025 |
| 1.4.0 | 8 | 03.06.2025 |
| 1.3.6 | 7 | 03.06.2025 |
| 1.3.5 | 7 | 03.06.2025 |
| 1.3.3 | 7 | 03.06.2025 |
| 1.3.2 | 7 | 03.06.2025 |
| 1.3.1 | 7 | 03.06.2025 |
| 1.3.0 | 8 | 03.06.2025 |
| 1.2.0 | 8 | 03.06.2025 |
| 1.1.14 | 8 | 20.02.2025 |
| 1.1.13 | 8 | 20.02.2025 |
| 1.1.12 | 10 | 20.02.2025 |
| 1.1.11 | 9 | 20.02.2025 |
| 1.1.10 | 8 | 20.02.2025 |
| 1.1.9 | 9 | 03.06.2025 |
| 1.1.8 | 7 | 03.06.2025 |
| 1.1.7 | 7 | 03.06.2025 |
| 1.1.6 | 7 | 03.06.2025 |
| 1.1.5 | 6 | 03.06.2025 |
| 1.1.3 | 7 | 03.06.2025 |
| 1.1.2 | 7 | 03.06.2025 |
| 1.1.1 | 8 | 03.06.2025 |
| 1.1.0 | 7 | 03.06.2025 |
| 1.0.0 | 9 | 03.06.2025 |