What is the best way to lock cache in asp.net? What is the best way to lock cache in asp.net? asp.net asp.net

What is the best way to lock cache in asp.net?


Here's the basic pattern:

  • Check the cache for the value, return if its available
  • If the value is not in the cache, then implement a lock
  • Inside the lock, check the cache again, you might have been blocked
  • Perform the value look up and cache it
  • Release the lock

In code, it looks like this:

private static object ThisLock = new object();public string GetFoo(){  // try to pull from cache here  lock (ThisLock)  {    // cache was empty before we got the lock, check again inside the lock    // cache is still empty, so retreive the value here    // store the value in the cache here  }  // return the cached value here}


For completeness a full example would look something like this.

private static object ThisLock = new object();...object dataObject = Cache["globalData"];if( dataObject == null ){    lock( ThisLock )    {        dataObject = Cache["globalData"];        if( dataObject == null )        {            //Get Data from db             dataObject = GlobalObj.GetData();             Cache["globalData"] = dataObject;        }    }}return dataObject;


There is no need to lock the whole cache instance, rather we only need to lock the specific key that you are inserting for. I.e. No need to block access to the female toilet while you use the male toilet :)

The implementation below allows for locking of specific cache-keys using a concurrent dictionary. This way you can run GetOrAdd() for two different keys at the same time - but not for the same key at the same time.

using System;using System.Collections.Concurrent;using System.Web.Caching;public static class CacheExtensions{    private static ConcurrentDictionary<string, object> keyLocks = new ConcurrentDictionary<string, object>();    /// <summary>    /// Get or Add the item to the cache using the given key. Lazily executes the value factory only if/when needed    /// </summary>    public static T GetOrAdd<T>(this Cache cache, string key, int durationInSeconds, Func<T> factory)        where T : class    {        // Try and get value from the cache        var value = cache.Get(key);        if (value == null)        {            // If not yet cached, lock the key value and add to cache            lock (keyLocks.GetOrAdd(key, new object()))            {                // Try and get from cache again in case it has been added in the meantime                value = cache.Get(key);                if (value == null && (value = factory()) != null)                {                    // TODO: Some of these parameters could be added to method signature later if required                    cache.Insert(                        key: key,                        value: value,                        dependencies: null,                        absoluteExpiration: DateTime.Now.AddSeconds(durationInSeconds),                        slidingExpiration: Cache.NoSlidingExpiration,                        priority: CacheItemPriority.Default,                        onRemoveCallback: null);                }                // Remove temporary key lock                keyLocks.TryRemove(key, out object locker);            }        }        return value as T;    }}