A lightweight, thread-safe, in-memory cache.
$ dotnet add package EviCacheEviCache is a lightweight, thread-safe, in-memory caching library for .NET.
It supports multiple eviction policies and offers extended cache operations. Moreover, it provides metrics and inspection capabilities.
using EviCache;
using EviCache.Enums;
using EviCache.Options;
// Create a cache with LRU and capacity 100
var cache = new Cache<string, string>(new CacheOptions(
capacity: 100,
evictionPolicy: EvictionPolicy.LRU));
// Put & Get
cache.Put("user:1", "André");
var name = cache.Get("user:1"); // "André"
// GetOrAdd (adds on miss, returns existing on hit)
var color = cache.GetOrAdd("color", "blue");
// Per-item absolute expiration (e.g., 1 minute)
cache.Put("otp", "123456",
new CacheItemOptions {
Expiration = new ExpirationOptions.Absolute(TimeSpan.FromMinutes(1))
});
// Same semantics, but async + cancellation support
await cache.PutAsync("k", "v", ct);
var value = await cache.GetAsync("k", ct);
var (found, v) = await cache.TryGetAsync("missing", ct);
Choose via CacheOptions.EvictionPolicy:
CacheFullException.When capacity is full, the cache evicts one candidate (if the policy allows). If no candidate can be evicted or policy is NoEviction, a CacheFullException is thrown with diagnostic data (capacity, attempted key, policy).
Set expiration globally (default for all items) or per item:
ExpirationOptions.Absolute(TimeSpan ttl): expires at now + ttl.ExpirationOptions.Sliding(TimeSpan ttl): expires if not accessed within ttl.ExpirationOptions.None: no expiration.// Global default expiration: absolute 10 minutes
var cache = new Cache<string, byte[]>(new CacheOptions(
100, EvictionPolicy.LFU,
new ExpirationOptions.Absolute(TimeSpan.FromMinutes(10))));
// Per-item sliding expiration: 5 minutes
cache.Put("session:12", session,
new CacheItemOptions {
Expiration = new ExpirationOptions.Sliding(TimeSpan.FromMinutes(5))
});
Expired items are purged lazily on access and during capacity checks; they won’t appear in
GetKeys()/GetSnapshot().
Get(key) → value (throws if missing).TryGet(key, out value) / TryGetAsync → no throw.ContainsKey(key) / ContainsKeyAsync → does not affect hit/miss counters.Put(key, value) / Put(key, value, options) → insert or update.AddOrUpdate(key, value) → inserts on miss; returns the provided value.GetOrAdd(key, value) → returns existing value on hit; otherwise, inserts and returns the provided value.Remove(key) → bool; Clear() → clears all.GetKeys() → ImmutableList<TKey> of non-expired keys (order depends on policy).GetSnapshot() → ImmutableList<KeyValuePair<TKey,TValue>> of non-expired entries.GetMetadata(key) / TryGetMetadata(key, out meta) → last access/update, access count, expiration, etc.Capacity, Count (purges expired first), Hits, Misses, Evictions.Get/TryGet/GetOrAdd (hit path) increments Hits and updates access metadata + policy structures.ContainsKey is intentionally “cold”: it checks existence without touching metrics or access ordering.All public operations are protected by an internal SemaphoreSlim, ensuring a single writer/reader critical section. Both sync and async APIs are safe to call concurrently.
IDisposable/IAsyncDisposable, it is disposed when the entry is removed, evicted, updated (when replacing a different instance), or during Clear().Clear() gathers disposables and disposes them in the background (respecting the provided cancellation token in ClearAsync). Errors during disposal are logged.Pass an ILogger to the constructor or rely on the default NullLogger. Notable events:
Information.Debug.Error.KeyNotFoundException: Get on a non-existing/expired key.CacheFullException: when full and:
NoEviction, orCacheFullException includes Capacity, the attempted key (if available), and EvictionPolicy for diagnostics.
GetKeys() returns a filtered, immutable snapshot without expired entries.Count triggers a purge of expired items before returning the size.ContainsKey as a pre-check before Get — do a single TryGet to minimize lock acquisitions.EviCache is released as open source under the MIT license. Bug reports and contributions are welcome at the GitHub repository.