• Skip to primary navigation
  • Skip to main content

Josh Withee

  • Home
  • Blog
  • Contact
  • Show Search
Hide Search

Simple C# Cache

Josh Withee · February 23, 2022 · Leave a Comment

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());

Related

Uncategorized C#, Cache

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Copyright © 2023