1
Vote

No ICache version of AspNetCache

description

The only implementation of the ICache was the InMemoryCache. I sleep better at night when I am relying on the a mature cache implementation such as the ASP.NET cache over something written inside the project.

Below is the implementation of ICache I am using:
    public class AspNetCache : ICache
    {
        private const string DependentEntitySetPrefix = "dependent_entity_set_";
        private HttpContext httpContext;
        private long cacheHits;
        private long cacheMisses;
        private long cacheAdds;
        private long cacheItemInvalidations;

        public long CacheHits { get { return cacheHits; } }
        public long CacheMisses { get { return cacheMisses; } }
        public long CacheItemAdds { get { return cacheAdds; } }
        public long CacheItemInvalidations { get { return cacheItemInvalidations; } }


        public AspNetCache() : this(null)
        {
        }

        public AspNetCache(HttpContext httpContext)
        {
            this.httpContext = httpContext;
        }

        private Cache HttpCache
        {
            get
            {
                if (httpContext != null)
                {
                    return httpContext.Cache;
                }

                var context = HttpContext.Current;
                if (context == null)
                {
                    throw new InvalidOperationException("Unable to determine HTTP context.");
                }

                return context.Cache;
            }
        }

        /// <summary>
        ///     Tries to the get entry by key.
        /// </summary>
        /// <param name="key">The cache key.</param>
        /// <param name="value">The retrieved value.</param>
        /// <returns>
        ///     A value of <c>true</c> if entry was found in the cache, <c>false</c> otherwise.
        /// </returns>
        public bool GetItem(string key, out object value)
        {
            key = GetCacheKey(key);
            value = HttpCache.Get(key);

            var result = value != null;
            if (!result)
                Interlocked.Increment(ref cacheMisses);
            else
                Interlocked.Increment(ref cacheHits);
            return result;
        }

        /// <summary>
        ///     Adds the specified entry to the cache.
        /// </summary>
        /// <param name="key">The entry key.</param>
        /// <param name="value">The entry value.</param>
        /// <param name="dependentEntitySets">The list of dependent entity sets.</param>
        /// <param name="slidingExpiration">The sliding expiration.</param>
        /// <param name="absoluteExpiration">The absolute expiration.</param>
        public void PutItem(string key, object value, IEnumerable<string> dependentEntitySets, TimeSpan slidingExpiration, DateTime absoluteExpiration)
        {
            key = GetCacheKey(key);
            var cache = HttpCache;

            foreach (var entitySet in dependentEntitySets)
            {
                EnsureEntryExists(DependentEntitySetPrefix + entitySet);
            }

            try
            {
                var cd = new CacheDependency(new string[0], dependentEntitySets.Select(c => DependentEntitySetPrefix + c).ToArray());
                cache.Insert(key, value, cd, absoluteExpiration, slidingExpiration, CacheItemPriority.Normal, null);
                Interlocked.Increment(ref cacheAdds);
            }
            catch (Exception)
            {
                // there's a possibility that one of the dependencies has been evicted by another thread
                // in this case just don't put this item in the cache
            }
        }

        /// <summary>
        ///     Invalidates all cache entries which are dependent on any of the specified entity sets.
        /// </summary>
        /// <param name="entitySets">The entity sets.</param>
        public void InvalidateSets(IEnumerable<string> entitySets)
        {
            foreach (string entitySet in entitySets)
            {
                HttpCache.Remove(DependentEntitySetPrefix + entitySet);
                Interlocked.Increment(ref this.cacheItemInvalidations);
            }
        }

        /// <summary>
        ///     Invalidates cache entry with a given key.
        /// </summary>
        /// <param name="key">The cache key.</param>
        public void InvalidateItem(string key)
        {
            key = GetCacheKey(key);
            HttpCache.Remove(key);
            Interlocked.Increment(ref this.cacheItemInvalidations);
        }

        /// <summary>
        ///     Hashes the query to produce cache key..
        /// </summary>
        /// <param name="query">The query.</param>
        /// <returns>Hashed query which becomes a cache key.</returns>
        private static string GetCacheKey(string query)
        {
            var bytes = Encoding.UTF8.GetBytes(query);
            var hashString = Convert.ToBase64String(MD5.Create().ComputeHash(bytes));
            return hashString;
        }

        private void EnsureEntryExists(string key)
        {
            var cache = HttpCache;

            if (cache.Get(key) == null)
            {
                try
                {
                    cache.Insert(key, key, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration);
                }
                catch (Exception)
                {
                    // ignore exceptions.
                }
            }
        }
    }

comments

pweissbrod wrote Oct 6, 2013 at 9:15 PM

Never mind, I found this in a separate DLL