public override IAsyncEnumerable <TResult> ExecuteAsync <TResult>(Expression query)
        {
            var asCachingExpressionVisitor = new AsCachingExpressionVisitor();

            query = asCachingExpressionVisitor.GetExtractAsCachingParameter(query, out bool asCaching, out CachingOptions options);

            if (!asCaching)
            {
                return(base.ExecuteAsync <TResult>(query));
            }

            var cacheKey = GetCacheKey(query);

            Task <List <TResult> > valuesTask;

            if (options.DistributedLock)
            {
                valuesTask = _cacheProvider.FetchObjectWithLockAsync(cacheKey, () => base.ExecuteAsync <TResult>(query).ToList(), options.Expiry);
            }
            else
            {
                valuesTask = _cacheProvider.FetchObjectAsync(cacheKey, () => base.ExecuteAsync <TResult>(query).ToList(), options.Expiry);
            }

            //Tricky implementation with custom implementation of AsyncEnumerable with by pass async function inside async iterator, and make code path async all the way down
            //If execute in current block, we can't await here, and any wait method will blocks thread.
            return(new MyAsyncEnumerable <TResult>(valuesTask));
        }
        public override TResult Execute <TResult>(Expression query)
        {
            var asCachingExpressionVisitor = new AsCachingExpressionVisitor();

            query = asCachingExpressionVisitor.GetExtractAsCachingParameter(query, out bool asCaching, out CachingOptions options);

            if (!asCaching)
            {
                return(base.Execute <TResult>(query));
            }

            var cacheKey = GetCacheKey(query);

            if (options.DistributedLock)
            {
                return(_cacheProvider.FetchObjectWithLock(cacheKey, () => base.Execute <TResult>(query), options.Expiry));
            }

            return(_cacheProvider.FetchObject(cacheKey, () => base.Execute <TResult>(query), options.Expiry));
        }
        public override async Task <TResult> ExecuteAsync <TResult>(Expression query, CancellationToken cancellationToken)
        {
            var asCachingExpressionVisitor = new AsCachingExpressionVisitor();

            query = asCachingExpressionVisitor.GetExtractAsCachingParameter(query, out bool asCaching, out CachingOptions options);

            if (!asCaching)
            {
                return(await base.ExecuteAsync <TResult>(query, cancellationToken));
            }

            var cacheKey = GetCacheKey(query);

            if (options.DistributedLock)
            {
                return(await _cacheProvider.FetchObjectWithLockAsync(cacheKey, () => base.ExecuteAsync <TResult>(query, cancellationToken), options.Expiry));
            }

            return(await _cacheProvider.FetchObjectAsync(cacheKey, () => base.ExecuteAsync <TResult>(query, cancellationToken), options.Expiry));
        }