https://github.com/soenneker/soenneker.utils.singletondictionary
An externally initializing singleton dictionary that uses double-check asynchronous locking, with optional async and sync disposal
https://github.com/soenneker/soenneker.utils.singletondictionary
async csharp dictionary dotnet lock singleton sync
Last synced: 6 months ago
JSON representation
An externally initializing singleton dictionary that uses double-check asynchronous locking, with optional async and sync disposal
- Host: GitHub
- URL: https://github.com/soenneker/soenneker.utils.singletondictionary
- Owner: soenneker
- License: mit
- Created: 2023-03-12T02:08:37.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-10-29T03:43:15.000Z (over 1 year ago)
- Last Synced: 2024-10-30T03:37:43.927Z (over 1 year ago)
- Topics: async, csharp, dictionary, dotnet, lock, singleton, sync
- Language: C#
- Homepage: https://soenneker.com
- Size: 651 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://www.nuget.org/packages/Soenneker.Utils.SingletonDictionary/)
[](https://github.com/soenneker/soenneker.utils.singletondictionary/actions/workflows/publish-package.yml)
[](https://www.nuget.org/packages/Soenneker.Utils.SingletonDictionary/)
#  Soenneker.Utils.SingletonDictionary
### A flexible singleton dictionary with double-check async locking, sync/async disposal, and external initialization
---
## Features
* ✅ Async and sync initialization patterns
* ✅ Optional cancellation support
* ✅ Async and sync access methods
* ✅ Fully disposable (sync and async)
* ✅ Thread-safe with `AsyncLock` from [Nito.AsyncEx](https://github.com/StephenCleary/AsyncEx)
---
## Installation
```bash
dotnet add package Soenneker.Utils.SingletonDictionary
```
---
## ✨ Example Usage
Here’s an example using `SingletonDictionary` to manage singleton `HttpClient` instances keyed by configuration (e.g. timeout):
```csharp
public class HttpRequester : IDisposable, IAsyncDisposable
{
private readonly SingletonDictionary _clients;
public HttpRequester()
{
_clients = new SingletonDictionary((args) =>
{
var socketsHandler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(10),
MaxConnectionsPerServer = 10
};
var client = new HttpClient(socketsHandler)
{
Timeout = TimeSpan.FromSeconds((int)args[0])
};
return client;
});
}
public async ValueTask Get()
{
var client = await _clients.Get("100", 100);
await client.GetAsync("https://google.com");
}
public async ValueTask DisposeAsync()
{
GC.SuppressFinalize(this);
await _clients.DisposeAsync();
}
public void Dispose()
{
GC.SuppressFinalize(this);
_clients.Dispose();
}
}
```
---
## 🔍 Internals
`SingletonDictionary` is backed by a `ConcurrentDictionary` and supports:
* Multiple constructor overloads for async/sync factory functions
* Internal double-checked locking on access
* Deferred/lazy factory execution
* Proper disposal of values (both sync and async interfaces)
* Safe mutation via `SetInitialization` before first use
Example constructor overloads include:
```csharp
new SingletonDictionary(Func> factory);
new SingletonDictionary(Func factory);
new SingletonDictionary(Func> factory);
// And more...
```
You can also initialize manually:
```csharp
var dict = new SingletonDictionary();
dict.SetInitialization((args) => new MyService(args));
```
---
## 🛡️ Thread Safety
This library uses [`AsyncLock`](https://github.com/StephenCleary/AsyncEx) for safe concurrent access in async contexts, and synchronously via `Lock()` for blocking methods. This avoids race conditions and guarantees safe singleton creation.