Soenneker.Dictionaries.SingletonKeys
4.0.134
Prefix Reserved
dotnet add package Soenneker.Dictionaries.SingletonKeys --version 4.0.134
NuGet\Install-Package Soenneker.Dictionaries.SingletonKeys -Version 4.0.134
<PackageReference Include="Soenneker.Dictionaries.SingletonKeys" Version="4.0.134" />
<PackageVersion Include="Soenneker.Dictionaries.SingletonKeys" Version="4.0.134" />
<PackageReference Include="Soenneker.Dictionaries.SingletonKeys" />
paket add Soenneker.Dictionaries.SingletonKeys --version 4.0.134
#r "nuget: Soenneker.Dictionaries.SingletonKeys, 4.0.134"
#:package Soenneker.Dictionaries.SingletonKeys@4.0.134
#addin nuget:?package=Soenneker.Dictionaries.SingletonKeys&version=4.0.134
#tool nuget:?package=Soenneker.Dictionaries.SingletonKeys&version=4.0.134
Soenneker.Dictionaries.SingletonKeys
Creates, caches, and owns one value per key, with asynchronous and synchronous factories plus coordinated eviction and disposal.
Installation
dotnet add package Soenneker.Dictionaries.SingletonKeys
Basic usage
using Soenneker.Dictionaries.SingletonKeys;
await using var clients = new SingletonKeyDictionary<string, ApiClient>(
async (tenantId, cancellationToken) =>
await ApiClient.Connect(tenantId, cancellationToken));
ApiClient client = await clients.Get("tenant-42", cancellationToken);
The first caller for a missing key runs its factory while concurrent callers for that key wait. A successful value is cached and returned until removal, clear, or disposal. Factories for different lock stripes can run concurrently; keys that hash to the same stripe temporarily serialize.
If a factory faults or is canceled, no value is cached and a later call can retry. The cancellation token of the creating call is passed to its factory; waiting callers can cancel while waiting for the key lock.
Initialization arguments
Use the T1 or T1, T2 variants when creation needs arguments supplied by Get:
var clients = new SingletonKeyDictionary<string, ApiClient, Uri, string>(
(tenantId, endpoint, apiKey, cancellationToken) =>
ApiClient.Connect(endpoint, apiKey, cancellationToken));
ApiClient client = await clients.Get(
"tenant-42",
endpoint,
apiKey,
cancellationToken);
Arguments are creation-only. Later calls for the same key receive the cached value even if they pass different arguments. The Func<T1> and Func<(T1, T2)> overloads defer argument construction until the key is known to be missing.
Factories can also be assigned once with SetInitialization, or with Initialize(state, static ...) to avoid capturing a closure. Configure initialization before concurrent use; changing the factory after one is set is rejected.
Removal choices
// Fast removal of an already cached value, including disposal:
bool removed = await clients.Remove("tenant-42", cancellationToken);
// Strong eviction that also waits behind an in-flight creation for this key:
bool evicted = await clients.Evict("tenant-42", cancellationToken);
Removeis the same fast path asTryRemoveAndDispose. It can returnfalsewhile a factory is still creating the key.Evictcoordinates with creation and is the appropriate choice when the key must be absent after the call.TryRemove(key, out value)does not dispose the value; ownership transfers to the caller.Clearcoordinates across all stripes, removes every cached value, and disposes them.
Synchronous counterparts are available, but they block when the configured factory or value disposal is asynchronous. Prefer the async APIs in request and worker code.
Snapshots and ownership
TryGet never initializes. GetAll, GetKeys, and GetValues acquire all stripes and return new collections representing a coordinated snapshot.
Cached values are dictionary-owned. Removal-with-disposal, clear, and dictionary disposal prefer IAsyncDisposable over IDisposable. Do not cache the same disposable instance under multiple keys unless repeated disposal is safe, and do not use a value after its key is evicted.
Disposal is terminal and waits for factories already running under a key stripe before disposing their results. Do not call dictionary disposal from inside one of its own factories, because the factory holds a stripe that disposal must acquire.
| 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
- Soenneker.Asyncs.Locks (>= 4.0.74)
- Soenneker.Atomics.ValueBools (>= 4.0.38)
- Soenneker.Enums.InitializationModes (>= 4.0.45)
- Soenneker.Extensions.Enumerable (>= 4.0.658)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Soenneker.Dictionaries.SingletonKeys:
| Package | Downloads |
|---|---|
|
Soenneker.Dictionaries.Singletons
An externally initializing singleton dictionary that uses double-check asynchronous locking, with optional async and sync disposal |
|
|
Soenneker.Google.Credentials
An async thread-safe singleton for Google OAuth credentials |
|
|
Soenneker.AngleSharp.Parser
A thread-safe cache for AngleSharp HtmlParser instances keyed by context type. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.134 | 9,096 | 8/31/2026 |
| 4.0.133 | 11,125 | 8/31/2026 |
| 4.0.132 | 66 | 8/31/2026 |
| 4.0.131 | 5,425 | 8/30/2026 |
| 4.0.130 | 325 | 8/30/2026 |
| 4.0.129 | 62 | 8/30/2026 |
| 4.0.128 | 11,077 | 8/30/2026 |
| 4.0.127 | 59 | 8/30/2026 |
| 4.0.125 | 6,872 | 8/29/2026 |
| 4.0.124 | 56 | 8/29/2026 |
| 4.0.123 | 56,609 | 8/26/2026 |
| 4.0.122 | 6,210 | 8/25/2026 |
| 4.0.121 | 35,042 | 8/21/2026 |
| 4.0.120 | 30,353 | 8/18/2026 |
| 4.0.119 | 26,191 | 8/12/2026 |
| 4.0.118 | 35,615 | 8/8/2026 |
| 4.0.117 | 12,047 | 8/8/2026 |
| 4.0.116 | 5,637 | 8/8/2026 |
| 4.0.115 | 33,644 | 8/7/2026 |
| 4.0.114 | 23,914 | 7/29/2026 |
Updated NuGet packages