public TResult UpdateCachedData <TResult>(ICacheSettings settings, MethodDelegate <TResult> @delegate, TResult data, params Object[] args)
        {
            // TODO: Support Locking Here

            var buffer = data;

            // Default cache settings
            if (settings == null)
            {
                settings = this.DefaultSettings;
            }

            #region Build CacheKey

            String cacheKey = settings.CacheKeyOverride;

            if (String.IsNullOrWhiteSpace(cacheKey))
            {
                cacheKey = this.BuildCacheKey(@delegate, args);
            }

            #endregion

            #region Cache Storage

#if !CACHENULLS
            // Early exit for empty result
            // TODO: Consider adding option to cache NULL data here
            if (buffer == null)
            {
                return(buffer = null);
            }
#endif

            #region Ensure we have a dependency and that we have a cachekey collection to add to

            settings.Dependencies           = settings.Dependencies ?? this.DefaultDependency;
            settings.Dependencies.CacheKeys = settings.Dependencies.CacheKeys ?? new String[] { };

            #endregion

            CacheItem cacheItem = null;

#if CACHENULLS
            if (buffer == null)
            {
                cacheItem = new CacheItem(cacheKey, TrackingCacheProvider.NULL_OBJECT, "CacheUtils");
            }
            else
#endif
            cacheItem = new CacheItem(cacheKey, buffer, "CacheUtils");

            // Save Data To Cache
            this._cache.Add(cacheItem, settings.GetCacheItemPolicy());

            #endregion

            return(buffer);
        }
        public TResult GetCachedData <TResult>(ICacheSettings settings, MethodDelegate <TResult> @delegate, params Object[] args)
        {
            // Default cache settings
            if (settings == null)
            {
                settings = this.DefaultSettings;
            }

            #region Build CacheKey

            String  cacheKey = settings.CacheKeyOverride;
            Boolean inLock   = false;

            if (String.IsNullOrWhiteSpace(cacheKey))
            {
                cacheKey = this.BuildCacheKey(@delegate, args);
            }

            #endregion

            // Add tracking key immediately
            // No point only adding it on success, because if there's an error, we don't want to cache the error state, so the dependency SHOULD fail
            this.AddTracking(cacheKey);

            #region Non Locking Cache Retrieval

            // Load data from cache
            Object buffer = this._cache.Get(cacheKey);

#if CACHENULLS
            if (buffer == TrackingCacheProvider.NULL_OBJECT)
            {
                return(default(TResult));
            }
#endif

            if (buffer is TResult)
            {
                return((TResult)buffer);
            }

            #endregion

            #region Locking Cache Retrieval

            if (buffer == null && ConfigurationManager.AppSettings["Dolkens.Framework.Caching.Lock"].ToBoolean(true))
            {
                if (this._lockTable[cacheKey] == null)
                {
                    // Only process a single thread for a particular cacheKey at a time
                    lock (this._lockTable)
                    {
                        if (this._lockTable[cacheKey] == null)
                        {
                            this._lockTable[cacheKey] = this._lockTable[cacheKey] ?? new Object();
                        }
                    }
                }

                inLock = Monitor.TryEnter(this._lockTable[cacheKey], settings.LockTimeout);

                buffer = this._cache.Get(cacheKey);

                if (buffer == TrackingCacheProvider.NULL_OBJECT)
                {
                    if (inLock)
                    {
                        Monitor.Exit(this._lockTable[cacheKey]);
                    }

                    return(default(TResult));
                }

                if (buffer is TResult)
                {
                    if (inLock)
                    {
                        Monitor.Exit(this._lockTable[cacheKey]);
                    }

                    return((TResult)buffer);
                }
            }

            #endregion

            #region Retrieve Fresh Data

            if (buffer == null)
            {
                // Swap in a clean tracking list
                IEnumerable <String> trackingList = this.SwapTracking(null);

                try
                {
                    buffer = @delegate(args);
                }
                catch (Exception ex)
                {
                    // Release the lock
                    if (inLock)
                    {
                        Monitor.Exit(this._lockTable[cacheKey]);
                    }

                    // Bubble exception
                    throw ex;
                }
                finally
                {
                    // Return tracking list to normal
                    trackingList = this.SwapTracking(trackingList);
                }

                #region Cache Storage

#if !CACHENULLS
                // Early exit for empty result
                // TODO: Consider adding option to cache NULL data here
                if (buffer == null)
                {
                    return(buffer = null);
                }
#endif

                #region Ensure we have a dependency and that we have a cachekey collection to add to

                settings.Dependencies           = settings.Dependencies ?? this.DefaultDependency;
                settings.Dependencies.CacheKeys = settings.Dependencies.CacheKeys ?? new String[] { };

                #endregion

                settings.Dependencies.CacheKeys = settings.Dependencies.CacheKeys.Union(trackingList).Distinct().ToArray();

                CacheItem cacheItem = null;

#if CACHENULLS
                if (buffer == null)
                {
                    cacheItem = new CacheItem(cacheKey, TrackingCacheProvider.NULL_OBJECT, "CacheUtils");
                }
                else
#endif
                cacheItem = new CacheItem(cacheKey, buffer, "CacheUtils");

                // Save Data To Cache
                this._cache.Add(cacheItem, settings.GetCacheItemPolicy());

                #endregion
            }

            #endregion

            // Release the lock
            if (inLock)
            {
                Monitor.Exit(this._lockTable[cacheKey]);
            }

            return((TResult)buffer);
        }