/// <summary>
        /// Retrieves an item from the cache, if possible.
        /// It'll lazily init the cache asynchronously and return null if necessary.
        /// It will always return in a timely manner; e.g., without waiting for a cache connection
        /// It will never propagate an exception, unless the throwExceptions flag is set on the constructor
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task <CacheResponseDto <T> > GetWithMetaDataAsync <T>(string key)
        {
            key = CleanKey(key);
            var dto = new CacheResponseDto <T>().StartTiming();

            try
            {
                if (!TryGetFromMemoryCache(key, ref dto)) // try memory cache first
                {
                    if (IsRedisAccessible())              // Couldn't get it from memory, so try redis
                    {
                        var byteBuffer = await _cacheDatabase.StringGetAsync(key);

                        if (!byteBuffer.IsNullOrEmpty)
                        {
                            Log(eCacheEvent.KeyValueGotFromCentralAttempt, key);
                            PostProcessRedisBuffer(key, ref dto, byteBuffer);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (_config.IsExceptionPropagationEnabled)
                {
                    throw;
                }
                Log(ex);
                dto.Exception = ex;
            }
            return(dto.StopTiming());
        }
        /// <summary>
        /// Retrieves an item from the cache, if possible.
        /// It'll lazily init the cache asynchronously and return null if necessary.
        /// It will always return in a timely manner; e.g., without waiting for a cache connection
        /// It will never propagate an exception, unless the throwExceptions flag is set on the constructor
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public CacheResponseDto <T> GetWithMetaData <T>(string key)
        {
            key = CleanKey(key);
            var dto = new CacheResponseDto <T>().StartTiming();

            try
            {
                if (!TryGetFromMemoryCache(key, ref dto))
                {
                    if (IsRedisAccessible())
                    {
                        var byteBuffer = _cacheDatabase.StringGet(key);
                        PostProcessRedisBuffer(key, ref dto, byteBuffer);
                    }
                }
            }
            catch (Exception ex)
            {
                Log(ex);
                dto.Exception = ex;
            }
            return(dto.StopTiming());
        }