DevelopmentHelpers.AzureDistributedCache
11.0.0
dotnet add package DevelopmentHelpers.AzureDistributedCache --version 11.0.0
NuGet\Install-Package DevelopmentHelpers.AzureDistributedCache -Version 11.0.0
<PackageReference Include="DevelopmentHelpers.AzureDistributedCache" Version="11.0.0" />
<PackageVersion Include="DevelopmentHelpers.AzureDistributedCache" Version="11.0.0" />
<PackageReference Include="DevelopmentHelpers.AzureDistributedCache" />
paket add DevelopmentHelpers.AzureDistributedCache --version 11.0.0
#r "nuget: DevelopmentHelpers.AzureDistributedCache, 11.0.0"
#:package DevelopmentHelpers.AzureDistributedCache@11.0.0
#addin nuget:?package=DevelopmentHelpers.AzureDistributedCache&version=11.0.0
#tool nuget:?package=DevelopmentHelpers.AzureDistributedCache&version=11.0.0
DevelopmentHelpers.AzureDistributedCache
A .NET library that simplifies Azure Redis Cache integration with support for both IDistributedCache and HybridCache APIs.
✨ Features
| Feature | IDistributedCache | HybridCache |
|---|---|---|
| Redis/Distributed Storage | ✅ | ✅ |
| In-Memory L1 Cache | ❌ | ✅ |
| Stampede Protection | ❌ | ✅ |
| Automatic Serialization | ❌ | ✅ |
| GetOrCreateAsync Pattern | ❌ | ✅ |
| Tag-based Invalidation | ❌ | ✅ |
| Structured Logging | ✅ | ✅ |
📦 Installation
dotnet add package DevelopmentHelpers.AzureDistributedCache
🚀 Quick Start
1. Add Configuration
Add to your appsettings.json:
{
"DevelopmentHelpers": {
"AzureDistributedCacheConfiguration": {
"DnsName": "your-cache.redis.cache.windows.net",
"ConnectionString": "your-cache.redis.cache.windows.net:6380,password=xxx,ssl=True,abortConnect=False"
},
"HybridCacheConfiguration": {
"DefaultExpiration": "00:05:00",
"DefaultLocalCacheExpiration": "00:02:00",
"MaximumPayloadBytes": 1048576,
"MaximumKeyLength": 1024
}
}
}
2. Register Services
Option A: HybridCache (Recommended)
// Program.cs
builder.Services.AddAzureHybridCache(builder.Configuration);
Option B: IDistributedCache Only
// Program.cs
builder.Services.AddAzureRedisCache(builder.Configuration);
3. Use in Your Code
HybridCache (Recommended Pattern)
public class ProductService
{
private readonly HybridCache _cache;
private readonly ILogger<ProductService> _logger;
public ProductService(HybridCache cache, ILogger<ProductService> logger)
{
_cache = cache;
_logger = logger;
}
public async Task<Product?> GetProductAsync(int productId, CancellationToken ct = default)
{
// HybridCache handles everything:
// ✓ Checks L1 (memory) then L2 (Redis)
// ✓ Calls factory only on cache miss
// ✓ Prevents stampede (parallel requests wait)
// ✓ Serializes/deserializes automatically
return await _cache.GetOrCreateAsync(
$"product:{productId}",
async token => await FetchProductFromDatabaseAsync(productId, token),
new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(10),
LocalCacheExpiration = TimeSpan.FromMinutes(2)
},
cancellationToken: ct);
}
public async Task InvalidateProductAsync(int productId)
{
await _cache.RemoveAsync($"product:{productId}");
_logger.LogInformation("Invalidated cache for product {ProductId}", productId);
}
}
IDistributedCache (Traditional Pattern)
public class ProductService
{
private readonly IDistributedCache _cache;
private readonly ILogger<ProductService> _logger;
public ProductService(IDistributedCache cache, ILogger<ProductService> logger)
{
_cache = cache;
_logger = logger;
}
public async Task<Product?> GetProductAsync(int productId, CancellationToken ct = default)
{
var cacheKey = $"product:{productId}";
// Check cache
var cached = await _cache.GetStringAsync(cacheKey, ct);
if (cached != null)
{
_logger.LogDebug("Cache hit for {CacheKey}", cacheKey);
return JsonSerializer.Deserialize<Product>(cached);
}
// Cache miss - fetch from database
_logger.LogDebug("Cache miss for {CacheKey}", cacheKey);
var product = await FetchProductFromDatabaseAsync(productId, ct);
if (product != null)
{
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
SlidingExpiration = TimeSpan.FromMinutes(2)
};
await _cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(product),
options,
ct);
}
return product;
}
}
⚙️ Configuration Options
AzureDistributedCacheConfiguration
| Property | Type | Description |
|---|---|---|
ConnectionString |
string | Redis connection string (required) |
DnsName |
string | Redis DNS name for logging (required) |
HybridCacheConfiguration
| Property | Type | Default | Description |
|---|---|---|---|
DefaultExpiration |
TimeSpan | 5 minutes | L2 (Redis) cache expiration |
DefaultLocalCacheExpiration |
TimeSpan | 5 minutes | L1 (memory) cache expiration |
MaximumPayloadBytes |
int | 1 MB | Maximum cache entry size |
MaximumKeyLength |
int | 1024 | Maximum cache key length |
📝 Registration Overloads
// From IConfiguration
services.AddAzureHybridCache(configuration);
services.AddAzureRedisCache(configuration);
// With explicit settings
services.AddAzureHybridCache(cacheSettings, hybridSettings);
services.AddAzureRedisCache(cacheSettings);
// With connection string
services.AddAzureHybridCache(connectionString, dnsName, hybridSettings);
services.AddAzureRedisCache(connectionString, dnsName);
📊 Logging
The library uses structured logging with ILogger<AzureRedisCache>. All cache operations are logged:
| Level | Events |
|---|---|
| Information | Connection success, cache set, cache remove |
| Debug | Cache hits, cache misses, operation details |
| Error | Connection failures, operation errors |
Configure logging in appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"DevelopmentHelpers.AzureDistributedCache": "Debug"
}
}
}
💡 Best Practices
Cache Key Design
// ✅ Good: Hierarchical, descriptive keys
var key = $"user:{userId}:profile";
var key = $"products:category:{categoryId}:page:{page}";
// ❌ Avoid: Generic, non-descriptive keys
var key = "data1";
var key = userId.ToString();
Expiration Strategy
// High-traffic, rarely changing data
new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromHours(1),
LocalCacheExpiration = TimeSpan.FromMinutes(10)
}
// Frequently updated data
new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(5),
LocalCacheExpiration = TimeSpan.FromMinutes(1)
}
Error Handling
public async Task<Product?> GetProductSafeAsync(int id)
{
try
{
return await _cache.GetOrCreateAsync(
$"product:{id}",
async ct => await _repository.GetAsync(id, ct));
}
catch (RedisConnectionException ex)
{
_logger.LogError(ex, "Redis unavailable, falling back to database");
return await _repository.GetAsync(id);
}
}
🎯 Sample Application
The included AzureRedisCache sample project demonstrates:
- Side-by-side comparison of IDistributedCache and HybridCache
- Modern Bootstrap 5 UI with cache hit/miss visualization
- Code examples and feature comparison table
- Clear cache functionality for testing
Run the sample:
cd AzureRedisCache
dotnet run
📚 API Reference
HybridCache Methods
Task<T> GetOrCreateAsync<T>(
string key,
Func<CancellationToken, Task<T>> factory,
HybridCacheEntryOptions? options = null,
CancellationToken token = default);
Task RemoveAsync(string key, CancellationToken token = default);
Task RemoveByTagAsync(string tag, CancellationToken token = default);
IDistributedCache Methods
Task<byte[]?> GetAsync(string key, CancellationToken token = default);
Task<string?> GetStringAsync(string key, CancellationToken token = default);
Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default);
Task SetStringAsync(string key, string value, DistributedCacheEntryOptions options, CancellationToken token = default);
Task RemoveAsync(string key, CancellationToken token = default);
Task RefreshAsync(string key, CancellationToken token = default);
🧪 Running Tests
dotnet test
Tests use real Redis integration (no mocks) for accurate behavior verification.
🔧 Troubleshooting
| Issue | Solution |
|---|---|
| Connection timeout | Check firewall rules, verify connection string |
| SSL/TLS errors | Ensure ssl=True in connection string for Azure Redis |
| Serialization errors | Ensure cached types are JSON-serializable |
| Large payload errors | Increase MaximumPayloadBytes or reduce object size |
📄 License
MIT License - see LICENSE.md
🤝 Contributing
- Fork the repository
- Create a feature branch
- Submit a pull request
See CONTRIBUTING.md for coding standards.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Caching.Hybrid (>= 9.4.0)
- Microsoft.Extensions.Configuration (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.9)
- StackExchange.Redis (>= 3.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 11.0.0 | 38 | 6/27/2026 |
| 10.0.1 | 243 | 12/21/2025 |
| 10.0.0 | 251 | 12/3/2025 |
| 9.0.3 | 198 | 9/26/2025 |
| 9.0.2 | 177 | 9/26/2025 |
| 9.0.1 | 314 | 8/8/2025 |
| 9.0.0 | 406 | 11/20/2024 |
| 8.0.1 | 224 | 11/14/2024 |
| 8.0.0 | 9,066 | 10/22/2024 |
| 7.0.8 | 5,800 | 2/16/2024 |
| 7.0.3 | 280 | 12/26/2023 |
| 4.0.1 | 574 | 12/13/2022 |
| 4.0.0 | 491 | 11/10/2022 |
| 3.0.0 | 1,086 | 11/29/2021 |
| 2.0.0 | 835 | 1/6/2020 |
| 1.0.0 | 779 | 1/6/2020 |
v11.0.0:
- Added HybridCache support with two-level caching (L1 in-memory + L2 Redis)
- Added stampede protection via HybridCache
- Added structured logging throughout AzureRedisCache class
- New AddAzureHybridCache() DI extension methods
- New HybridCacheConfiguration for customizable cache options
- Updated sample app with modern Bootstrap 5 UI
- Comprehensive README with best practices and examples