A plug-n-play thread-safe cache that can be dropped into any C# application.
using System;
using System.Collections.Concurrent;
using System.Text.Json;
namespace MyNamespace
{
public class MiniCache<T>
{
public MiniCache(int secondsToLive, Func<T, T> cloneFunction = null, bool active = true)
{
if (typeof(T).IsAbstract || typeof(T).IsInterface)
throw new Exception("Cannot cache " + typeof(T).ToString() + " because it is not instantiable.");
this.secondsToLive = secondsToLive;
this.cloneFunction = cloneFunction;
if (cloneFunction == null)
if (typeof(T).IsValueType)
this.cloneFunction = x => x;
else
#warning It would be better to provide an efficient clone function, but this will get you by in a pinch.
this.cloneFunction = x => JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(x));
this.active = active;
}
private readonly bool active;
private readonly Func<T, T> cloneFunction;
private readonly int secondsToLive;
private ConcurrentDictionary<string, CacheObject<T>> store = new ConcurrentDictionary<string, CacheObject<T>>();
public bool TryGet(string key, out T value)
{
if (active && store.TryGetValue(key, out CacheObject<T> val))
{
if (DateTime.Now > val.expiration)
{
value = default(T);
return false;
}
value = cloneFunction(val.value);
if (ReferenceEquals(value, val.value))
throw new Exception("cloneFunction returned a reference to the object in cache. This leads to a corrupt cache. Supply a Func which deep-clones the object.");
return true;
}
value = default(T);
return false;
}
public void Put(string key, T value)
{
if (active)
store[key] = new CacheObject<T>(cloneFunction(value), this.secondsToLive);
}
class CacheObject<K>
{
public K value;
public DateTime expiration;
public CacheObject(K value, int secondsToLive)
{
this.value = value;
expiration = DateTime.Now.AddSeconds(secondsToLive);
}
}
}
}
Notes:
- The
active
parameter exists for easy disabling of the cache - If you are caching objects (i.e.
T
is a reference type) you should supply a clone function that is more efficient than the default serialize-deserialize strategy this uses.
Examples:
// cache System.Data.DataTable objects
MiniCache<DataTable> dtCache = new MiniCache<DataTable>(20, x => x.Copy());
Leave a Reply