/// <summary>
        /// BBernard
        /// Add or update the cache with the specified cache key and item that will be Lazy Initialized from Lambda function/logic.
        /// This method ensures that the item is initialized with full thread safety and that only one thread ever executes the work
        /// to initialize the item to be cached -- significantly improving server utilization and performance.
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="key"></param>
        /// <param name="fnValueFactory"></param>
        /// <param name="cacheItemPolicy"></param>
        /// <returns></returns>
        public static TValue GetOrAddFromCache <TKey, TValue>(TKey key, Func <TValue> fnValueFactory, CacheItemPolicy cacheItemPolicy)
            where TValue : class
        {
            TValue result = LazyCachePolicy.IsPolicyEnabled(cacheItemPolicy)
                                ? (TValue)_lazyCache.GetOrAddFromCache(key, fnValueFactory, cacheItemPolicy)
                                : fnValueFactory();

            return(result);
        }
        /// <summary>
        /// Helper method to more easily create an Absolute Expiration CacheItemPolicy directly from a Configuration
        /// Parameter name that has the TTL Seconds; with Default fallback if it does not exist.
        /// </summary>
        /// <param name="ttlSecondsConfigKey"></param>
        /// <param name="callbackWhenCacheEntryRemoved"></param>
        /// <returns></returns>
        public static CacheItemPolicy NewAbsoluteExpirationPolicy(string ttlSecondsConfigKey, Action <CacheEntryRemovedArguments> callbackWhenCacheEntryRemoved = null)
        {
            var timeSpanToLive = LazyCacheConfig.GetCacheTTLFromConfig(ttlSecondsConfigKey, LazyCacheConfig.NeverCacheTTL);

            if (timeSpanToLive.TotalMilliseconds > 0)
            {
                return(LazyCachePolicy.NewAbsoluteExpirationPolicy(timeSpanToLive, callbackWhenCacheEntryRemoved));
            }

            return(null);
        }
        /// <summary>
        /// Helper method to more easily create an Absolute Expiration CacheItemPolicy directly from a Configuration
        /// Parameter names that has the TTL Seconds; this will return the first identified valid configuration key.
        /// </summary>
        /// <param name="ttlConfigKeysToSearch"></param>
        /// <param name="callbackWhenCacheEntryRemoved"></param>
        /// <returns></returns>
        public static CacheItemPolicy NewAbsoluteExpirationPolicy(string[] ttlConfigKeysToSearch, Action <CacheEntryRemovedArguments> callbackWhenCacheEntryRemoved = null)
        {
            var timeSpanToLive = LazyCacheConfig.GetCacheTTLFromConfig(ttlConfigKeysToSearch, LazyCacheConfig.NeverCacheTTL);

            if (timeSpanToLive.TotalMilliseconds > 0)
            {
                return(LazyCachePolicy.NewAbsoluteExpirationPolicy(timeSpanToLive, callbackWhenCacheEntryRemoved));
            }

            //Finally return the cache policy for the first identified valid configuration key.
            return(null);
        }
        /// <summary>
        /// BBernard
        /// Add or update the cache with the specified cache key and item that will be Lazy Initialized Asynchronously from Lambda function/logic.
        /// This method ensures that the item is initialized with full thread safety and that only one thread ever executes the work
        /// to initialize the item to be cached -- significantly improving server utilization and performance.
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="key"></param>
        /// <param name="fnAsyncValueFactory"></param>
        /// <param name="cacheItemPolicy"></param>
        /// <returns></returns>
        public static async Task <TValue> GetOrAddFromCacheAsync <TKey, TValue>(TKey key, Func <Task <TValue> > fnAsyncValueFactory, CacheItemPolicy cacheItemPolicy)
            where TValue : class
        {
            //Because the underlying cache is set up to store any object and the async coercion isn't as easy as the synchronous,
            //  we must wrap the original generics typed async factory into a new Func<> that matches the required type.
            var wrappedFnValueFactory = new Func <Task <object> >(async() => await fnAsyncValueFactory());

            TValue result = LazyCachePolicy.IsPolicyEnabled(cacheItemPolicy)
                                ? (TValue)await _lazyCache.GetOrAddFromCacheAsync(key, wrappedFnValueFactory, cacheItemPolicy)
                                : await fnAsyncValueFactory();

            return(result);
        }