PLC.DistributedDictionary
1.0.3
dotnet add package PLC.DistributedDictionary --version 1.0.3
NuGet\Install-Package PLC.DistributedDictionary -Version 1.0.3
<PackageReference Include="PLC.DistributedDictionary" Version="1.0.3" />
<PackageVersion Include="PLC.DistributedDictionary" Version="1.0.3" />
<PackageReference Include="PLC.DistributedDictionary" />
paket add PLC.DistributedDictionary --version 1.0.3
#r "nuget: PLC.DistributedDictionary, 1.0.3"
#:package PLC.DistributedDictionary@1.0.3
#addin nuget:?package=PLC.DistributedDictionary&version=1.0.3
#tool nuget:?package=PLC.DistributedDictionary&version=1.0.3
PLC.DistributedDictionary (NuGet) / PLC.Shared.DistributedConcurrentDictionary (namespace)
Target frameworks: net8.0, net9.0, net10.0 (one assembly per TFM in the NuGet package).
Distributed dictionary for .NET, backed by:
ZiggyCreatures.FusionCachefor fast L1/L2 cache readsRedLock.netfor cross-node write locking- Redis
SETindex forCount,Keys,Values, and key existence - Policy-driven resilience (retry + circuit breaker + degraded mode)
- Health snapshot and telemetry callback for microservice observability
Implemented interfaces:
IDictionary<TKey, TValue>IReadOnlyDictionary<TKey, TValue>IDictionary(non-generic)
Install
dotnet add package PLC.DistributedDictionary
Namespaces and types remain under PLC.Shared.DistributedConcurrentDictionary (e.g. using PLC.Shared.DistributedConcurrentDictionary;).
Quick Start
Minimal setup — default KeyPrefix is dcd, Redis retry/circuit defaults apply, FailureMode is FailFast:
using PLC.Shared.DistributedConcurrentDictionary;
using RedLockNet;
using RedLockNet.SERedis;
using RedLockNet.SERedis.Configuration;
using StackExchange.Redis;
using ZiggyCreatures.Caching.Fusion;
using ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379");
FusionCache cache = new FusionCache(new FusionCacheOptions());
IDistributedLockFactory locks = RedLockFactory.Create(new[] { new RedLockMultiplexer(redis) });
JsonFusionRedisDistributedDictionary<string, MyValue> dict =
new JsonFusionRedisDistributedDictionary<string, MyValue>(cache, redis, locks);
dict["a"] = new MyValue();
bool found = dict.TryGetValue("a", out MyValue? value);
FusionRedisDictionaryHealthSnapshot health = dict.GetHealthSnapshot();
Optional: isolate keys in Redis and relax failure handling (e.g. keep serving cache when Redis is down):
var dict = new JsonFusionRedisDistributedDictionary<string, MyValue>(
cache,
redis,
locks,
new FusionRedisDictionaryOptions
{
KeyPrefix = "myapp:dict",
FailureMode = FusionRedisFailureMode.ReadOnlyStale,
OnTelemetry = static e => Console.WriteLine($"{e.EventType} | {e.Message}"),
});
Topics: register once, use by name
Step 1 — Program.cs (or DI): build shared FusionCache, Redis, RedLock once, plus a DistributedTopicDictionaries with a base KeyPrefix (e.g. myapp).
using ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379");
FusionCache cache = new FusionCache(new FusionCacheOptions());
IDistributedLockFactory locks = RedLockFactory.Create(new[] { new RedLockMultiplexer(redis) });
DistributedTopicDictionaries topics = new DistributedTopicDictionaries(
cache,
redis,
locks,
new FusionRedisDictionaryOptions { KeyPrefix = "myapp" });
// keep `topics` in DI / static / host service for the app lifetime
Step 2 — anywhere: resolve the same hub, then call GetOrAdd<TKey,TValue>(topic). The first call for that topic + type pair creates the dictionary; later calls return the same instance. Redis keyspace is myapp:{topic}:… per topic.
JsonFusionRedisDistributedDictionary<string, OrderDto> orders =
topics.GetOrAdd<string, OrderDto>("orders");
orders[orderId] = dto;
Use a distinct topic string per logical stream (e.g. "orders", "invoices"). The pair (topic, TKey, TValue) identifies the singleton; e.g. GetOrAdd<string, OrderDto>("orders") and GetOrAdd<string, InvoiceDto>("orders") are two different dictionaries.
ASP.NET / Microsoft.Extensions.DependencyInjection
Step 1 — Program.cs: register the node once (DictionaryDistributedNodeOptions via lambda):
using Microsoft.Extensions.DependencyInjection;
using PLC.Shared.DistributedConcurrentDictionary;
builder.Services.AddDistributedDictionaryNode(node =>
{
node.RedisConnectionString = builder.Configuration["Redis"]!;
node.ConfigureFusionCacheOptions = fc => { /* FusionCacheOptions */ };
node.ConfigureDistributedDictionary = o =>
{
o.KeyPrefix = "myapp";
o.FailureMode = FusionRedisFailureMode.ReadOnlyStale;
o.OnTelemetry = static e => { /* ... */ };
};
});
If RedisConnectionString is omitted, register IConnectionMultiplexer yourself before this call.
Step 2 — any service: inject IDistributedDictionaryFactory and open as many topics as needed:
public sealed class OrderService(IDistributedDictionaryFactory dicts)
{
public void Save(string id, OrderDto dto) => dicts.GetOrAdd<string, OrderDto>("orders")[id] = dto;
public void SaveInvoice(string id, InvoiceDto inv) => dicts.GetOrAdd<string, InvoiceDto>("invoices")[id] = inv;
}
Failure/Policy Modes
FailFast: throw immediately when Redis/lock fails.ReadOnlyStale: keep serving cache reads when possible, while infra is unhealthy.DataCriticality(Critical,Operational,Reference) tunes default retry/circuit profile.
Health + Telemetry
GetHealthSnapshot()exposes degraded state, circuit status, failure counters, and last failure.OnTelemetrycallback emits operation-level events:- read/write success
- redis failure
- lock failure
- circuit opened/blocked
- degraded reads
Notes
TKeyandTValueare JSON-serialized.- Read path is optimized for cache (
TryGetfrom FusionCache). - Writes (
Add, indexer set,Remove,Clear) are lock-protected. - For best consistency across nodes, use the same Redis and same
KeyPrefix.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.1)
- RedLock.net (>= 2.3.2)
- StackExchange.Redis (>= 2.10.1)
- ZiggyCreatures.FusionCache (>= 2.6.0)
-
net8.0
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- RedLock.net (>= 2.3.2)
- StackExchange.Redis (>= 2.10.1)
- ZiggyCreatures.FusionCache (>= 2.6.0)
-
net9.0
- Microsoft.Extensions.DependencyInjection (>= 9.0.1)
- RedLock.net (>= 2.3.2)
- StackExchange.Redis (>= 2.10.1)
- ZiggyCreatures.FusionCache (>= 2.6.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.