A cache item object that keeps some details about the data to be cached
        /// <summary>
        /// Save the return data from invocation to cache
        /// </summary>
        /// <param name="methodExecutedContext"></param>
        public override void OnMethodExecuted(MethodExecutedContext methodExecutedContext)
        {
            if (Duration > 0 && !methodExecutedContext.TryGet<bool>(Global.__flatwhite_outputcache_restored))
            {
                var key = methodExecutedContext.TryGet<string>(Global.__flatwhite_outputcache_key);
                if (string.IsNullOrWhiteSpace(key))
                {
                    return;
                }

                var cacheStore = methodExecutedContext.TryGet<ICacheStore>(Global.__flatwhite_outputcache_store);
                var strategy = methodExecutedContext.TryGet<ICacheStrategy>(Global.__flatwhite_outputcache_strategy);
                var cacheItem = new CacheItem
                {
                    Key = key,
                    Data = methodExecutedContext.Result,
                    StoreId = cacheStore.StoreId,
                    StaleWhileRevalidate = StaleWhileRevalidate,
                    MaxAge = Duration,
                    CreatedTime = DateTime.UtcNow
                };
                CreatePhoenix(methodExecutedContext.Invocation, cacheItem);

                var changeMonitors = strategy.GetChangeMonitors(methodExecutedContext.Invocation, methodExecutedContext.InvocationContext);
                foreach (var mon in changeMonitors)
                {
                    mon.CacheMonitorChanged += x =>
                    {
                        RefreshCache(key);
                    };
                }

                cacheStore.Set(key, cacheItem, DateTime.UtcNow.AddSeconds(Duration + StaleWhileRevalidate));
            }
        }
        /// <summary>
        /// Create the phoenix object which can refresh the cache itself if StaleWhileRevalidate > 0
        /// and store by key in Global.Cache.Phoenix
        /// </summary>
        /// <param name="invocation"></param>
        /// <param name="cacheItem"></param>
        /// <returns></returns>
        private void CreatePhoenix(_IInvocation invocation, CacheItem cacheItem)
        {
            var cacheInfo = new CacheInfo
            {
                CacheKey = cacheItem.Key,
                CacheStoreId = cacheItem.StoreId,
                CacheDuration = Duration,
                StaleWhileRevalidate = StaleWhileRevalidate,
                AutoRefresh = AutoRefresh
            };

            if (Global.Cache.PhoenixFireCage.ContainsKey(cacheItem.Key))
            {
                Global.Cache.PhoenixFireCage[cacheItem.Key].Dispose();
            }

            Global.Cache.PhoenixFireCage[cacheItem.Key] = new Phoenix(invocation, cacheInfo); ;
        }
        /// <summary>
        /// Create the phoenix object which can refresh the cache itself if StaleWhileRevalidate > 0
        /// and store by key in Global.Cache.Phoenix
        /// </summary>
        /// <param name="invocation"></param>
        /// <param name="cacheItem"></param>
        /// <returns></returns>
        private void CreatePhoenix(_IInvocation invocation, CacheItem cacheItem)
        {
            if (cacheItem.StaleWhileRevalidate <= 0)
            {
                return;
            }

            Phoenix phoenix;
            if (Global.Cache.PhoenixFireCage.TryGetValue(cacheItem.Key, out phoenix))
            {
                phoenix?.Dispose();
            }

            Global.Cache.PhoenixFireCage[cacheItem.Key] = new Phoenix(invocation, cacheItem);
        }