/// <summary>
        /// Sets the item with the given key.
        /// </summary>
        /// <typeparam name="T">The item type.</typeparam>
        /// <param name="cache">The <see cref="IDistributedCache"/>.</param>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        /// <param name="methodCacheOptions">Optional method cache options for configuring cache expiry.</param>
        /// <param name="cancellationToken">A cancellation token to cancel running tasks with.</param>
        /// <returns>A boolean indicating whether the item was written to the cache or not.</returns>
        public static async Task <bool> SetAsync <T>(
            this IDistributedCache cache,
            string key,
            T value,
            IMethodCacheOptions methodCacheOptions = null,
            CancellationToken cancellationToken    = default
            )
        {
            byte[] cacheBytes;

            using (var memoryStream = new MemoryStream())
            {
                await JsonSerializer.SerializeAsync(memoryStream, value, methodCacheOptions?.SerializeType ?? typeof(T), JsonSerializerOptions, cancellationToken);

                memoryStream.Position = 0;
                cacheBytes            = memoryStream.ToArray();
            }

            if (cacheBytes.Length == 0)
            {
                return(false);
            }

            var cacheEntryOptions = new DistributedCacheEntryOptions();

            if (methodCacheOptions != null)
            {
                var expiration = new TimeSpan(
                    methodCacheOptions.ExpirationHours,
                    methodCacheOptions.ExpirationMinutes,
                    methodCacheOptions.ExpirationSeconds
                    );

                if (expiration.Ticks > 0)
                {
                    if (methodCacheOptions.SlidingExpiration)
                    {
                        cacheEntryOptions.SlidingExpiration = expiration;
                    }
                    else
                    {
                        cacheEntryOptions.AbsoluteExpirationRelativeToNow = expiration;
                    }
                }
            }

            await cache.SetAsync(
                key,
                cacheBytes,
                cacheEntryOptions,
                cancellationToken
                );

            return(true);
        }
        /// <summary>
        /// Gets or creates a memory cache entry.
        /// </summary>
        /// <typeparam name="T">The result type.</typeparam>
        /// <param name="method">The method.</param>
        /// <param name="arguments">The method arguments.</param>
        /// <param name="methodCacheOptions">The <see cref="IMethodCacheOptions"/>.</param>
        /// <param name="factory">The result factory.</param>
        /// <returns>The existing or created memory cache entry value.</returns>
        public async Task <T> GetOrCreate <T>(MethodInfo method, object[] arguments, IMethodCacheOptions methodCacheOptions, Func <Task <T> > factory)
        {
            var invocationKey = await CacheKeyProvider.GetCacheKey(method, arguments);

            try
            {
                var cacheEntry = await DistributedCache.GetAsync <T>(invocationKey, methodCacheOptions.SerializeType);

                if (cacheEntry.Loaded)
                {
                    return(cacheEntry.Value);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Error while reading value with '{Key}' from distributed cache", invocationKey);
            }

            T value = await factory.Invoke();

            var cacheSet = false;

            try
            {
                cacheSet = await DistributedCache.SetAsync(invocationKey, value, methodCacheOptions);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Error while writing value with key '{Key}' to distributed cache", invocationKey);
            }

            if (!cacheSet)
            {
                return(value);
            }

            await UpdateMethodKeys(invocationKey);

            return(value);
        }