CasCap.Common.Caching
4.17.0
Prefix Reserved
dotnet add package CasCap.Common.Caching --version 4.17.0
NuGet\Install-Package CasCap.Common.Caching -Version 4.17.0
<PackageReference Include="CasCap.Common.Caching" Version="4.17.0" />
<PackageVersion Include="CasCap.Common.Caching" Version="4.17.0" />
<PackageReference Include="CasCap.Common.Caching" />
paket add CasCap.Common.Caching --version 4.17.0
#r "nuget: CasCap.Common.Caching, 4.17.0"
#:package CasCap.Common.Caching@4.17.0
#addin nuget:?package=CasCap.Common.Caching&version=4.17.0
#tool nuget:?package=CasCap.Common.Caching&version=4.17.0
CasCap.Common.Caching
Multi-tier distributed caching library implementing the cache-aside pattern with support for Memory, Disk, and Redis cache layers.
Installation
dotnet add package CasCap.Common.Caching
Target frameworks: netstandard2.0, net8.0, net9.0, net10.0
Usage
For local memory caching, disable the remote tier explicitly:
using System;
using CasCap.Common.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AppCache = CasCap.Common.Abstractions.IDistributedCache;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddCasCapCaching(options =>
options.RemoteCache = options.RemoteCache with { IsEnabled = false });
using var host = builder.Build();
await host.StartAsync();
var cache = host.Services.GetRequiredService<AppCache>();
await cache.Set("greeting", "Hello", slidingExpiration: TimeSpan.FromMinutes(5));
Console.WriteLine(await cache.Get<string>("greeting")); // Hello
await host.StopAsync();
CasCap.Common.Abstractions.IDistributedCache
is the object-cache API, not Microsoft's byte-cache
interface with the same name. Get<T> can take an asynchronous factory for a cache miss. Concurrent
misses for the same key run the factory once per application instance, and once across instances when
DistributedLockingEnabled is set. Waiting callers re-read the populated entry. If the distributed lock
times out, the factory still runs so the caller never receives a spurious miss. Set,
Delete and DeleteAll affect the enabled layers.
To use application configuration, replace the registration with
builder.Services.AddCasCapCaching(builder.Configuration). The section defaults to
CasCap:CachingConfig. Overloads also accept a CachingConfig instance or an Action<CachingConfig>.
A non-empty connection string opens Redis during registration and returns its ConnectionMultiplexer
for reuse. Without one, registration supplies NullRemoteCache; it does not automatically turn off
RemoteCache.IsEnabled.
Select memory or disk with the LocalCacheType argument. This selects the implementation;
enabling DiskCache in JSON alone does not switch from memory to disk.
Cache Layers
| Service | Description |
|---|---|
DistributedCacheService |
Reads local first, then an enabled remote tier, then an optional factory; supports opt-in Redis locking |
RedisCacheService |
IRemoteCache implementation wrapping StackExchange.Redis with optional Lua script support |
NullRemoteCache |
No-op IRemoteCache implementation used for local-only caching. Redis infrastructure properties throw NotSupportedException |
MemoryCacheService |
ILocalCache implementation backed by IMemoryCache |
DiskCacheService |
ILocalCache implementation persisting cache entries to disk |
CacheExpiryBgService |
Coordinates expiry subscribers when Redis is registered and CacheExpiryServiceEnabled is true |
LocalCacheExpiryService |
Handles local cache entry expiration. Disable via LocalCacheExpiryServiceEnabled |
RemoteCacheExpiryService |
Handles remote cache entry expiration. Disable via RemoteCacheExpiryServiceEnabled |
Leadership Election
The current checkout also provides
ILeadershipRunner on .NET 8 and later. This API is provisional
and not yet available in the published package; use local Debug project references while validating
consumers, without publishing a package for each contract refinement.
Inject the runner into a background feature and await
RunAsync("example-worker", RunProtectedWorkAsync, stoppingToken). All replicas of the same worker
must use the same logical resource. RunProtectedWorkAsync(CancellationToken) must stay active until
cancelled, pass its token to all I/O and child work, and await cleanup before returning.
- Registration uses the existing Redis connection, database, key format and distributed-lock factory.
Configure Redis and
DistributedLockingEnabled: true. The runner is registered only when a lock factory is available; an elected consumer without those prerequisites fails dependency resolution rather than running unprotected. Local-only caching and disabled locking remain usable with startup dependency validation enabled, without registering an elected consumer. - Leadership uses
Redlock.GetTimings(RedlockProfiles.LeaderElection); short cache-miss locks still use root timings and never enter the election loop. Redlock.LeadershipCheckIntervalMsdefaults to1000, and controls lease-state observation only. RedLock.net renews the lease. A failed acquisition waits the profile retry interval before contending again.- Observed loss cancels the worker, awaits its cleanup, disposes the lease, then reacquires. Graceful shutdown awaits cleanup before releasing a still-held lease.
- Worker failures and unexpected normal completion propagate instead of restarting silently. Cleanup has no runner timeout: configure the host and container termination budgets explicitly. An uncooperative worker is not abandoned and no replacement run starts while it is still active.
- Lease polling has a detection delay; it is not downstream fencing or an exactly-once guarantee.
All caching registration overloads expose the same CachingConfig.Redlock instance through
IOptions<RedlockConfig>. Repeated registration preserves the first configuration. Root timings,
named profile timings and the leadership check interval are validated through the caching options.
Configuration
| Type | Description |
|---|---|
CachingConfig |
Main configuration record — RemoteCacheConnectionString, PubSubPrefix, MemoryCacheSizeLimit, UseBuiltInLuaScripts, DiskCacheFolder, ExpirationSyncMode, DistributedLockingEnabled, CacheAsideDisabled, CacheKeyFormat, HealthCheckRedis, CacheExpiryServiceEnabled, LocalCacheExpiryServiceEnabled, RemoteCacheExpiryServiceEnabled, Redlock |
RedlockConfig |
Timing parameters for Redis distributed locks with named profile support — root defaults (ExpiryMs 5s, WaitMs 5s, RetryMs 250ms) tuned for cache-miss protection. RedisKeyFormat for lock key prefixing. Built-in LeaderElection profile (30s/60s/5s) for long-lived locks. Custom profiles via Profiles dictionary |
RedlockProfiles |
Well-known profile name constants — CacheMiss, LeaderElection |
RedlockTimingProfile |
Timing values for a single named lock profile — ExpiryMs, WaitMs, RetryMs |
CacheParameters |
Per-layer enablement, database, startup clearing and serialization |
CacheParameters exposes IsEnabled, DatabaseId, ClearOnStartup and SerializationType;
entry lifetimes are passed to Get or Set, not stored in this record.
ClearOnStartup defaults to false. Enabling it can delete existing cache data.
Important defaults:
| Setting | Default |
|---|---|
RemoteCacheConnectionString |
null; no Redis registration |
MemoryCache.IsEnabled, DiskCache.IsEnabled, RemoteCache.IsEnabled |
true; select and configure layers deliberately |
MemoryCache.SerializationType |
None |
DiskCache.SerializationType |
Json |
RemoteCache.SerializationType |
MessagePack |
LocalCacheInvalidationEnabled |
true |
CacheExpiryServiceEnabled, LocalCacheExpiryServiceEnabled, RemoteCacheExpiryServiceEnabled |
true |
ExpirationSyncMode |
None |
DistributedLockingEnabled, UseBuiltInLuaScripts, CacheAsideDisabled |
false |
HealthCheckRedis |
Liveness; Redis health-check registration is .NET 8+ only |
MemoryCacheSizeLimit |
null; no configured limit |
CacheKeyFormat |
{0} |
Redlock.LeadershipCheckIntervalMs |
1000; .NET 8+ leadership observation only |
PubSubPrefix distinguishes this instance's own invalidation messages; choose a distinct value per
instance when overriding it. HealthCheckRedis: None suppresses Redis check registration.
Serialization and Expiration Modes
| Enum | Values |
|---|---|
CacheType |
None, Memory, Disk, Redis |
SerializationType |
None, Json, MessagePack |
ExpirationSyncType |
None, ExpireViaPubSub, ExtendRemoteExpiry |
Service Architecture
Multi-tier caching infrastructure with cache-aside pattern:
flowchart TD
CLIENT["Application Code"]
subgraph CachingServices["Caching Services"]
DIST["DistributedCacheService<br/>(Multi-tier orchestrator)"]
subgraph LocalCaches["Local Cache Implementations"]
MEM["MemoryCacheService<br/>(IMemoryCache)"]
DISK["DiskCacheService<br/>(File system)"]
end
subgraph RemoteCaches["Remote Cache"]
REDIS["RedisCacheService<br/>(StackExchange.Redis)"]
NULL_REMOTE["NullRemoteCache<br/>(Local-only no-op)"]
end
subgraph BackgroundServices["Background Services"]
EXPIRY_BG["CacheExpiryBgService"]
LOCAL_EXP["LocalCacheExpiryService"]
REMOTE_EXP["RemoteCacheExpiryService"]
end
end
REDIS_SERVER[("Redis Server<br/>(Pub/Sub)")]
LOCK["AsyncDuplicateLock<br/>(In-process coordination)"]
REDLOCK["IDistributedLockFactory<br/>(RedLock.net — optional)"]
LEADER["LeadershipRunner<br/>(.NET 8+)"]
CLIENT --> DIST
DIST -->|Choose one local implementation| MEM
DIST -->|Choose one local implementation| DISK
DIST --> REDIS
DIST -.local-only.-> NULL_REMOTE
DIST -.uses.-> LOCK
DIST -."opt-in distributed lock".-> REDLOCK
CLIENT --> LEADER
LEADER --> REDLOCK
REDIS <--> REDIS_SERVER
REDLOCK -."Redlock algorithm".-> REDIS_SERVER
EXPIRY_BG --> LOCAL_EXP
EXPIRY_BG --> REMOTE_EXP
REDIS_SERVER -."Pub/Sub expiry sync".-> LOCAL_EXP
REDIS_SERVER -."Pub/Sub expiry sync".-> REMOTE_EXP
LOCAL_EXP -.expires.-> MEM
LOCAL_EXP -.expires.-> DISK
REMOTE_EXP -.expires.-> REDIS
Configuration Examples
The configuration overload reads CasCap:CachingConfig. Omitted properties retain record defaults.
The examples below are alternatives, not a set of sections to merge indiscriminately.
Minimal — local memory cache only
{
"CasCap": {
"CachingConfig": {
"RemoteCache": {
"IsEnabled": false
}
}
}
}
Redis remote cache with JSON serialization
{
"CasCap": {
"CachingConfig": {
"RemoteCacheConnectionString": "redis.example.com:6379,abortConnect=false",
"RemoteCache": {
"SerializationType": "Json"
}
}
}
}
Disabling the expiry background services
Each background service can be switched off independently. CacheExpiryServiceEnabled: false prevents the CacheExpiryBgService hosted service being registered at all; the two granular toggles keep the hosted service registered but skip the individual subscriber.
{
"CasCap": {
"CachingConfig": {
"RemoteCacheConnectionString": "redis.example.com:6379,abortConnect=false",
"CacheExpiryServiceEnabled": true,
"LocalCacheExpiryServiceEnabled": false,
"RemoteCacheExpiryServiceEnabled": true
}
}
}
Distributed locking enabled (cache-miss protection)
{
"CasCap": {
"CachingConfig": {
"RemoteCacheConnectionString": "redis.example.com:6379,abortConnect=false",
"DistributedLockingEnabled": true,
"RemoteCache": {
"SerializationType": "Json"
}
}
}
}
Distributed locking with custom Redlock profiles
{
"CasCap": {
"CachingConfig": {
"RemoteCacheConnectionString": "redis.example.com:6379,abortConnect=false",
"DistributedLockingEnabled": true,
"RemoteCache": {
"SerializationType": "Json"
},
"Redlock": {
"ExpiryMs": 5000,
"WaitMs": 5000,
"RetryMs": 250,
"Profiles": {
"LeaderElection": {
"ExpiryMs": 30000,
"WaitMs": 60000,
"RetryMs": 5000
}
}
}
}
}
}
Extended Configuration
{
"CasCap": {
"CachingConfig": {
"RemoteCacheConnectionString": "redis.example.com:6379,abortConnect=false,connectTimeout=1000",
"DistributedLockingEnabled": true,
"CacheKeyFormat": "Example:{0}",
"MemoryCacheSizeLimit": 500,
"UseBuiltInLuaScripts": true,
"DiskCacheFolder": "./cache",
"ExpirationSyncMode": "ExpireViaPubSub",
"MemoryCache": {
"IsEnabled": true,
"ClearOnStartup": false,
"SerializationType": "None"
},
"DiskCache": {
"IsEnabled": false,
"SerializationType": "Json"
},
"RemoteCache": {
"IsEnabled": true,
"DatabaseId": 1,
"ClearOnStartup": false,
"SerializationType": "MessagePack"
},
"Redlock": {
"RedisKeyFormat": "Example:RedLock:{0}",
"ExpiryMs": 5000,
"WaitMs": 5000,
"RetryMs": 250,
"Profiles": {
"LeaderElection": {
"ExpiryMs": 30000,
"WaitMs": 60000,
"RetryMs": 5000
},
"LongRunningJob": {
"ExpiryMs": 120000,
"WaitMs": 30000,
"RetryMs": 10000
}
}
}
}
}
}
Replace the example endpoint and supply authentication through a secret provider, not tracked JSON.
For Redis expiration housekeeping, enable expired-key event notifications on the server, such as
notify-keyspace-events Ex. Run the application host so its enabled subscribers execute.
Distributed locking coordinates factory execution across instances but is not a guarantee of
exactly-once work. Handle a missing cache result if lock acquisition fails. Use CacheAsideDisabled
to call the factory directly; sets and deletes then become no-ops.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- CasCap.Common.Abstractions (>= 4.17.0)
- CasCap.Common.Extensions (>= 4.17.0)
- CasCap.Common.Extensions.Diagnostics.HealthChecks (>= 4.17.0)
- CasCap.Common.Logging (>= 4.17.0)
- CasCap.Common.Serialization.Json (>= 4.17.0)
- CasCap.Common.Serialization.MessagePack (>= 4.17.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.13)
- Microsoft.Extensions.Configuration.Json (>= 10.0.13)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.13)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.13)
- RedLock.net (>= 2.3.2)
- StackExchange.Redis (>= 3.4.20)
-
net10.0
- AspNetCore.HealthChecks.Redis (>= 9.0.0)
- CasCap.Common.Abstractions (>= 4.17.0)
- CasCap.Common.Extensions (>= 4.17.0)
- CasCap.Common.Extensions.Diagnostics.HealthChecks (>= 4.17.0)
- CasCap.Common.Logging (>= 4.17.0)
- CasCap.Common.Serialization.Json (>= 4.17.0)
- CasCap.Common.Serialization.MessagePack (>= 4.17.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.13)
- Microsoft.Extensions.Configuration.Json (>= 10.0.13)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.13)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.13)
- RedLock.net (>= 2.3.2)
- StackExchange.Redis (>= 3.4.20)
-
net8.0
- AspNetCore.HealthChecks.Redis (>= 9.0.0)
- CasCap.Common.Abstractions (>= 4.17.0)
- CasCap.Common.Extensions (>= 4.17.0)
- CasCap.Common.Extensions.Diagnostics.HealthChecks (>= 4.17.0)
- CasCap.Common.Logging (>= 4.17.0)
- CasCap.Common.Serialization.Json (>= 4.17.0)
- CasCap.Common.Serialization.MessagePack (>= 4.17.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.13)
- Microsoft.Extensions.Configuration.Json (>= 10.0.13)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.13)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.13)
- RedLock.net (>= 2.3.2)
- StackExchange.Redis (>= 3.4.20)
-
net9.0
- AspNetCore.HealthChecks.Redis (>= 9.0.0)
- CasCap.Common.Abstractions (>= 4.17.0)
- CasCap.Common.Extensions (>= 4.17.0)
- CasCap.Common.Extensions.Diagnostics.HealthChecks (>= 4.17.0)
- CasCap.Common.Logging (>= 4.17.0)
- CasCap.Common.Serialization.Json (>= 4.17.0)
- CasCap.Common.Serialization.MessagePack (>= 4.17.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.13)
- Microsoft.Extensions.Configuration.Json (>= 10.0.13)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.13)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.13)
- RedLock.net (>= 2.3.2)
- StackExchange.Redis (>= 3.4.20)
NuGet packages (8)
Showing the top 5 NuGet packages that depend on CasCap.Common.Caching:
| Package | Downloads |
|---|---|
|
CasCap.Common.AI
AI and LLM integration abstractions for OpenAI, Ollama, and Model Context Protocol. |
|
|
CasCap.Api.Knx
.NET client library for KNX building automation — connects via KNXnet/IP tunnelling or routing, monitors group-address telegrams, and fans events out to configurable sinks. |
|
|
CasCap.Api.DoorBird
.NET client library for DoorBird IP video door station — captures doorbell, motion and RFID events via the local LAN API and dispatches them to configurable sinks. |
|
|
CasCap.Api.DoorBird.Sinks
Pluggable event sink implementations (Redis, Azure Tables, Azure Blob Storage) for the CasCap.Api.DoorBird door station integration library. |
|
|
CasCap.Api.Fronius.Sinks
Pluggable event sink implementations (Redis, Azure Tables) for the CasCap.Api.Fronius solar inverter integration library. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.17.0 | 117 | 10/24/2026 |
| 4.16.0 | 188 | 10/21/2026 |
| 4.15.0 | 141 | 10/6/2026 |
| 4.14.4 | 152 | 10/4/2026 |
| 4.14.3 | 151 | 9/28/2026 |
| 4.14.2 | 178 | 9/25/2026 |
| 4.14.1 | 188 | 9/23/2026 |
| 4.14.0 | 471 | 9/19/2026 |
| 4.13.0 | 145 | 9/15/2026 |
| 4.12.8 | 262 | 9/10/2026 |
| 4.12.7 | 210 | 9/4/2026 |
| 4.12.6 | 147 | 9/1/2026 |
| 4.12.5 | 208 | 8/20/2026 |
| 4.12.4 | 248 | 8/18/2026 |
| 4.12.3 | 304 | 7/15/2026 |
| 4.12.2 | 318 | 6/26/2026 |
| 4.12.1 | 304 | 6/14/2026 |
| 4.12.0 | 289 | 6/12/2026 |
| 4.11.9 | 295 | 6/10/2026 |
| 4.11.8 | 294 | 5/27/2026 |