private async Task <TReturn> GetAndMaybeReturnAsync <TReturn>(string key, DeserializeDelegate <TReturn> deserializeDelegate, TId id = default(TId), CancellationToken token = default(CancellationToken))
        {
            var byteArray = await Cache.GetAsync(key, token);

            if (byteArray == null)
            {
                return(default(TReturn));
            }
            var cacheEnvelope     = SerializingSupport.Deserialize <CacheEnvelope>(byteArray);
            var cacheItemStrategy = Equals(id, default(TId)) ? GetCacheItemStrategy(cacheEnvelope) : await GetCacheItemStrategyAsync(id, cacheEnvelope, token);

            switch (cacheItemStrategy)
            {
            case UseCacheStrategyEnum.Use:
                return(deserializeDelegate(cacheEnvelope));

            case UseCacheStrategyEnum.Ignore:
                return(default(TReturn));

            case UseCacheStrategyEnum.Remove:
                await Cache.RemoveAsync(key, token);

                return(default(TReturn));

            default:
                FulcrumAssert.Fail($"Unexpected value of {nameof(UseCacheStrategyEnum)} ({cacheItemStrategy}).");
                return(default(TReturn));
            }
        }
        /// <summary>
        /// Get a value from the cache if all constraints are fulfilled.
        /// </summary>
        private async Task <TModel> CacheGetAsync(TId id, string key, CancellationToken token = default(CancellationToken))
        {
            if (UseCacheAtAllMethodAsync != null && !await UseCacheAtAllMethodAsync(typeof(TModel)))
            {
                return(default(TModel));
            }
            key = key ?? GetCacheKeyFromId(id);

            return(await GetAndMaybeReturnAsync(key, cacheEnvelope => SerializingSupport.Deserialize <TModel>(cacheEnvelope.Data), id, token));
        }
        /// <summary>
        /// Get a value from the cache if all constraints are fulfilled.
        /// </summary>
        protected internal async Task <PageEnvelope <TModel> > CacheGetAsync(int offset, int limit, string keyPrefix, CancellationToken token)
        {
            if (UseCacheAtAllMethodAsync != null &&
                !await UseCacheAtAllMethodAsync(typeof(PageEnvelope <TModel>)))
            {
                return(null);
            }
            var key = GetCacheKeyForPage(keyPrefix, offset, limit);

            return(await GetAndMaybeReturnAsync(key, cacheEnvelope => SerializingSupport.Deserialize <PageEnvelope <TModel> >(cacheEnvelope.Data), token : token));
        }
        /// <summary>
        /// Serialize the <paramref name="item"/>, put it into an envelope and serialize the envelope
        /// </summary>
        public byte[] ToSerializedCacheEnvelope <T>(T item)
        {
            var serializedItem = SerializingSupport.Serialize(item);
            var cacheEnvelope  = new CacheEnvelope
            {
                CacheIdentity = CacheIdentity,
                UpdatedAt     = DateTimeOffset.Now,
                Data          = serializedItem
            };
            var serializedCacheEnvelope = SerializingSupport.Serialize(cacheEnvelope);

            return(serializedCacheEnvelope);
        }
        /// <summary>
        /// Get a value from the cache if all constraints are fulfilled.
        /// </summary>
        protected internal async Task <TModel[]> CacheGetAsync(int limit, string key, CancellationToken token)
        {
            InternalContract.RequireGreaterThan(0, limit, nameof(limit));
            if (limit > _limitOfItemsInReadAllCache)
            {
                return(null);
            }
            if (UseCacheAtAllMethodAsync != null &&
                !await UseCacheAtAllMethodAsync(typeof(TModel[])))
            {
                return(null);
            }

            return(await GetAndMaybeReturnAsync(key, cacheEnvelope =>
            {
                var array = SerializingSupport.Deserialize <TModel[]>(cacheEnvelope.Data);
                if (limit > array.Length)
                {
                    return array;
                }
                var subset = array.Take(limit);
                return subset as TModel[] ?? subset.ToArray();
            }, token : token));
        }