PLC.DistributedDictionary 1.0.3

dotnet add package PLC.DistributedDictionary --version 1.0.3
                    
NuGet\Install-Package PLC.DistributedDictionary -Version 1.0.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PLC.DistributedDictionary" Version="1.0.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PLC.DistributedDictionary" Version="1.0.3" />
                    
Directory.Packages.props
<PackageReference Include="PLC.DistributedDictionary" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PLC.DistributedDictionary --version 1.0.3
                    
#r "nuget: PLC.DistributedDictionary, 1.0.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PLC.DistributedDictionary@1.0.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PLC.DistributedDictionary&version=1.0.3
                    
Install as a Cake Addin
#tool nuget:?package=PLC.DistributedDictionary&version=1.0.3
                    
Install as a Cake Tool

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.FusionCache for fast L1/L2 cache reads
  • RedLock.net for cross-node write locking
  • Redis SET index for Count, 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.
  • OnTelemetry callback emits operation-level events:
    • read/write success
    • redis failure
    • lock failure
    • circuit opened/blocked
    • degraded reads

Notes

  • TKey and TValue are JSON-serialized.
  • Read path is optimized for cache (TryGet from FusionCache).
  • Writes (Add, indexer set, Remove, Clear) are lock-protected.
  • For best consistency across nodes, use the same Redis and same KeyPrefix.
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
1.0.3 115 4/21/2026
1.0.2 103 4/18/2026
1.0.1 101 4/18/2026