/// <summary>
        /// Returns a new query where the result will be cached base on the <see cref="TimeSpan"/> parameter.
        /// </summary>
        /// <typeparam name="T">The type of entity being queried.</typeparam>
        /// <param name="source">The source query.</param>
        /// <param name="options">Options how to handle cached query results.</param>
        /// <returns>A new query where the result set will be cached.</returns>
        public static IQueryable <T> Cacheable <T>(this IQueryable <T> source, [NotParameterized] CacheableOptions options)
        {
            Check.NotNull(source, nameof(source));
            Check.NotNull(options, nameof(options));

            return
                (source.Provider is EntityQueryProvider
                   ? source.Provider.CreateQuery <T>(
                     Expression.Call(
                         instance: null,
                         method: CacheableMethodInfo.MakeGenericMethod(typeof(T)),
                         arg0: source.Expression,
                         arg1: Expression.Constant(options)))
                   : source);
        }
        private static Boolean ShouldResultBeCached <TResult>(TResult result, CacheableOptions options)
        {
            if (!options.CacheNullResult && result == null)
            {
                return(false);
            }

            if (result is IEnumerable)
            {
                var enumerable = result as IEnumerable;

                return(enumerable.Any());
            }

            return(true);
        }
        private static async Task <TResult> ExecuteSingletonAsyncQuery <TResult>(
            QueryContext queryContext,
            Func <QueryContext, IAsyncEnumerable <TResult> > compiledQuery,
            IDiagnosticsLogger <DbLoggerCategory.Query> logger,
            Type contextType,
            Func <object, Exception, string> logFormatter,
            ICacheProvider cacheProvider,
            object queryKey,
            CacheableOptions options)
        {
            try
            {
                var asyncEnumerable = compiledQuery(queryContext);

                using (var asyncEnumerator = asyncEnumerable.GetEnumerator())
                {
                    await asyncEnumerator.MoveNext(queryContext.CancellationToken);

                    if (cacheProvider != null)
                    {
                        // add query result to cache
                        if (ShouldResultBeCached(asyncEnumerator.Current, options))
                        {
                            cacheProvider.SetCachedResult <TResult>(queryKey, asyncEnumerator.Current, options.TimeToLive);
                        }

                        logger.Logger.Log <object>(LogLevel.Debug, CacheableEventId.QueryResultCached, queryKey, null, logFormatter);
                    }

                    return(asyncEnumerator.Current);
                }
            }
            catch (Exception exception)
            {
                logger.QueryIterationFailed(contextType, exception);

                throw;
            }
        }