DotnetDevkit.Cache
10.0.0
dotnet add package DotnetDevkit.Cache --version 10.0.0
NuGet\Install-Package DotnetDevkit.Cache -Version 10.0.0
<PackageReference Include="DotnetDevkit.Cache" Version="10.0.0" />
<PackageVersion Include="DotnetDevkit.Cache" Version="10.0.0" />
<PackageReference Include="DotnetDevkit.Cache" />
paket add DotnetDevkit.Cache --version 10.0.0
#r "nuget: DotnetDevkit.Cache, 10.0.0"
#:package DotnetDevkit.Cache@10.0.0
#addin nuget:?package=DotnetDevkit.Cache&version=10.0.0
#tool nuget:?package=DotnetDevkit.Cache&version=10.0.0
DotnetDevkit.Cache
Two packages that provide a small caching abstraction and a resilient Redis-backed implementation with distributed locking.
Packages at a glance
- DotnetDevkit.Cache.Abstractions
ICacheinterface forGetAsync,SetAsync,RemoveAsync, andGetOrAddAsyncwith cancellation support.CacheResult<T>value-or-miss struct withHasValue,Value,Some,None, and implicit conversion fromT.
- DotnetDevkit.Cache
RedisCacheimplementsICacheusingStackExchange.Rediswith JSON serialization viaSystem.Text.Json.GetOrAddAsyncguarded byRedLockNetdistributed locks to avoid thundering herd while still returning data if Redis is unavailable.- Graceful degradation: cache misses are returned instead of exceptions when Redis is down; factories still run.
RedisCacheConnectionServicebackground worker keeps the connection warm with health checks and exponential backoff retries.SafeRedisCacheOptionsextendsRedisCacheOptionswith sensible defaults for expiry, locking, and reconnection.
Installation
Add the packages you need from NuGet:
dotnet add package DotnetDevkit.Cache.Abstractions
# plus the Redis implementation
dotnet add package DotnetDevkit.Cache
Configure and wire up RedisCache
Register the cache in DI and configure connection + behavior knobs.
using DotnetDevkit.Cache;
using DotnetDevkit.Cache.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using StackExchange.Redis;
var builder = Host.CreateApplicationBuilder(args);
// Option 1: bind from configuration
builder.Services.Configure<SafeRedisCacheOptions>(
builder.Configuration.GetSection("RedisCache"));
// Option 2: configure in code
builder.Services.Configure<SafeRedisCacheOptions>(o =>
{
o.Configuration = "localhost:6379"; // from RedisCacheOptions
o.InstanceName = "devkit:";
o.DefaultExpiryMs = TimeSpan.FromMinutes(10).TotalMilliseconds;
o.LockExpiryMs = TimeSpan.FromSeconds(30).TotalMilliseconds;
o.LockWaitMs = TimeSpan.FromSeconds(10).TotalMilliseconds;
});
// Connection factory used by RedisCache and the background service
builder.Services.AddSingleton<Func<Task<ConnectionMultiplexer?>>>(_ =>
() => ConnectionMultiplexer.ConnectAsync("localhost:6379"));
builder.Services.AddSingleton<RedisCache>();
builder.Services.AddSingleton<ICache>(sp => sp.GetRequiredService<RedisCache>());
builder.Services.AddHostedService<RedisCacheConnectionService>();
await builder.Build().RunAsync();
Using the cache abstraction
ICache gives typed access with explicit cache-miss semantics.
public class UserProfileService(ICache cache)
{
public async Task<UserProfile?> TryGetProfileAsync(string userId, CancellationToken ct)
{
var cached = await cache.GetAsync<UserProfile>($"user:{userId}", ct);
return cached.HasValue ? cached.Value : null;
}
public Task CacheProfileAsync(string userId, UserProfile profile, CancellationToken ct) =>
cache.SetAsync($"user:{userId}", profile, TimeSpan.FromMinutes(30), ct);
public Task EvictProfileAsync(string userId, CancellationToken ct) =>
cache.RemoveAsync($"user:{userId}", ct);
}
Get-or-add with distributed lock
GetOrAddAsync prevents thundering herd with RedLock and still returns data when Redis is unavailable.
public class PricingService(ICache cache)
{
public Task<PriceCard> GetPriceAsync(string sku, CancellationToken ct) =>
cache.GetOrAddAsync(
key: $"price:{sku}",
factory: async token => await FetchPriceFromUpstreamAsync(sku, token),
absoluteExpiry: TimeSpan.FromMinutes(5),
cancellationToken: ct);
}
Behavior notes:
- If the value exists, it is returned immediately.
- If Redis is connected, a distributed lock on
${key}:lockis attempted to serialize factory execution. - If lock acquisition fails within the configured wait window, or Redis is down, the factory still runs and its result is returned (best-effort set when possible).
Options
SafeRedisCacheOptions derives from RedisCacheOptions, so all standard StackExchange.Redis configuration applies (Configuration, ConfigurationOptions, InstanceName, etc.). Additional knobs:
DefaultExpiryMs(default 5 minutes) — TTL used whenabsoluteExpiryis not provided.LockExpiryMs(default 30 seconds) — lease time for distributed locks.LockWaitMs(default 10 seconds) — maximum time spent trying to acquire the lock before falling back.HealthCheckIntervalSec(default 30 seconds) — delay between connection health checks once connected.RetryBaseDelayMs(default 2 seconds) — initial exponential backoff delay for reconnects.RetryMaxDelayMs(default 10 seconds) — cap for reconnect backoff.
Behavior highlights
- JSON serialization uses
System.Text.Jsonwith default options; primitives and reference types are supported. - All operations are cancellation-aware;
GetreturnsCacheResult.Noneon cache miss or connectivity issues. - Background connection loop performs health checks and retries with jitter to stabilize Redis connectivity.
- Disposal cleans up the Redis multiplexer, RedLock factory, and semaphores.
Abstractions reference
ICache— async cache contract with optional expiry per call andGetOrAddAsyncfactory helper.CacheResult<T>— explicit cache hit/miss wrapper withHasValueand implicit conversion fromTfor convenience.
Testing
See tests/DotnetDevkit.Cache.Test for coverage of connection handling, locking behavior, and option defaults.
| 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
- DotnetDevkit.Cache.Abstractions (>= 10.0.0)
- FakeItEasy (>= 8.3.0)
- Microsoft.Extensions.Caching.StackExchangeRedis (>= 10.0.0)
- Microsoft.Extensions.Configuration (>= 10.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.0)
- RedLock.net (>= 2.3.2)
- StackExchange.Redis (>= 2.10.1)
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 |
|---|---|---|
| 10.0.0 | 202 | 11/27/2025 |