Exemplo n.º 1
0
        /// <summary>
        /// Reset the timer on a specific cache item. If it still exists in cache, reset it.
        /// An exception is thrown when no item with the specify key is found.
        /// </summary>
        /// <param name="key">The unique key used to identify the item</param>
        /// <exception cref="System.Exception">Thrown when no item with the specified key is found</exception>
        public virtual void Reset(string key)
        {
            this.EvictExpiredItems();
            Lazy <CacheItem> lazyItem;

            this.InternalCache.TryGetValue(key, out lazyItem);
            //try to reset this cache item's timer.
            if (lazyItem != null)
            {
                //A race condition could exist that would allow it to be in cache above, but then missing here. So eat any exception where that would happen
                try
                {
                    var cacheItem = lazyItem.Value;
                    lock (this.EvictionOrderList)
                    {
                        EvictionOrderList.Remove(cacheItem.EvictionDate, cacheItem);
                        cacheItem.Reset();
                        EvictionOrderList.Add(cacheItem.EvictionDate, cacheItem);
                    }
                }
                catch (Exception)
                {
                    throw new Exception("No item with that key could be found");
                }
            }
            else
            {
                throw new Exception("No item with that key could be found");
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Gets the value with specified the key. If no item with that key exists,
        /// the factory function is called to construct a new value.
        ///
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">The unique key used to identify the item.</param>
        /// <param name="factory">A factory function that is called when the item is not in the
        /// cache, and a new copy of the item needs to be generated.</param>
        /// <param name="millisecondsToLive">The number of milliseconds that this item should remain in the cache.
        ///     If null, the item will live in cache indefinitely .
        /// </param>
        /// <returns>The item from cache</returns>
        public virtual async Task <T> ResolveAsync <T>(string key, Func <Task <T> > factory, int?millisecondsToLive = null)
        {
            this.EvictExpiredItems();

            var createdThisCall = false;

            millisecondsToLive = millisecondsToLive != null ? millisecondsToLive : this.DefaultMillisecondsToLive;
            var lazyCacheItem = this.InternalCache.GetOrAdd(key, (string k) =>
            {
                //only allow a single thread to run this specific resolver function at a time.
                var lazyResult = new Lazy <CacheItem>(() =>
                {
                    createdThisCall = true;

                    var lazyCacheItemValue = new Lazy <object>(() =>
                    {
                        var factoryValue = factory();
                        return(factoryValue);
                    });
                    var constructedCacheItem = new CacheItem(key, lazyCacheItemValue, millisecondsToLive);
                    return(constructedCacheItem);
                }, LazyThreadSafetyMode.ExecutionAndPublication);
                return(lazyResult);
            });

            CacheItem cacheItem = null;

            try
            {
                //get the cache item from lazy
                cacheItem = lazyCacheItem.Value;

                //force the cache item to run its lazy value factory
                var value = cacheItem.Value;

                //wait for the task to complete (which also propagates any exception)
                await(Task <T>) value;
            }
            catch (System.InvalidOperationException e)
            {
                this.Remove(key);
                throw new Exception("Possible recursive resolve() detected", e);
            }
            catch (ThreadAbortException e)
            {
                this.Remove(key);
                throw new Exception("Possible recursive resolve() detected", e);
            }
            catch (System.Exception e)
            {
                this.Remove(key);
                throw e;
            }
            finally
            {
            }

            //add this item to the eviction keys list
            lock (this.EvictionOrderList)
            {
                EvictionOrderList.Add(cacheItem.EvictionDate, cacheItem);
            }

            //if the item expired and was NOT created this call, toss it and get a new one. Will RARELY happen.
            if (cacheItem.IsExpired && createdThisCall == false)
            {
                this.Remove(key);
                return(await this.ResolveAsync(key, factory, millisecondsToLive));
            }
            else
            {
                var task   = (Task <T>)cacheItem.Value;
                var result = await task;

                //if this cache item should be discarded
                if (cacheItem != null && cacheItem.ShouldBeDiscarded)
                {
                    //use the newest value from cache (if it exists)
                    Lazy <CacheItem> lazyNewerCacheItem;
                    this.InternalCache.TryGetValue(key, out lazyNewerCacheItem);
                    if (lazyNewerCacheItem != null && lazyNewerCacheItem.IsValueCreated)
                    {
                        result = await(Task <T>) lazyNewerCacheItem.Value.Value;
                    }
                    else
                    {
                        //throw an exception because this item was discarded and no newer value exists
                        throw new Exception($"Could not retrieve item with key '{key}' because it was removed before resolver function finished processing");
                    }
                }
                return(result);
            }
        }