public override TResult Execute <TResult>(Expression query)
        {
            Check.NotNull(query, nameof(query));

            var queryContext = _queryContextFactory.Create();

            // search for cacheable operator and extract parameter
            var cachableExpressionVisitor = new CachableExpressionVisitor();

            query = cachableExpressionVisitor.GetExtractCachableParameter(query, out bool isCacheable, out CacheableOptions options);


            query = _queryModelGenerator.ExtractParameters(_logger, query, queryContext);

            // if cacheable operator is part of the query use cache logic
            if (isCacheable)
            {
                // generate key to identify query
                var queryKey = _cacheProvider.CreateQueryKey(query, queryContext.ParameterValues);

                if (_cacheProvider.TryGetCachedResult <TResult>(queryKey, out TResult cacheResult))
                {
                    _logger.Logger.Log <object>(LogLevel.Debug, CacheableEventId.CacheHit, queryKey, null, _logFormatter);

                    //cache was hit, so return cached query result
                    return(cacheResult);
                }
                else // cache was not hit
                {
                    var cacheKey      = _compiledQueryCacheKeyGenerator.GenerateCacheKey(query, false);
                    var compiledQuery = _compiledQueryCache.GetOrAddQuery(cacheKey, () => CompileQueryCore <TResult>(query, _queryModelGenerator, _database, _logger, _contextType));

                    // excecute query
                    var queryResult = compiledQuery(queryContext);

                    // add query result to cache
                    if (ShouldResultBeCached(queryResult, options))
                    {
                        _cacheProvider.SetCachedResult <TResult>(queryKey, queryResult, options.TimeToLive);
                    }

                    _logger.Logger.Log <object>(LogLevel.Debug, CacheableEventId.QueryResultCached, queryKey, null, _logFormatter);

                    return(queryResult);
                }
            }
            else
            {
                // return default query result
                var cacheKey      = _compiledQueryCacheKeyGenerator.GenerateCacheKey(query, false);
                var compiledQuery = _compiledQueryCache.GetOrAddQuery(cacheKey, () => CompileQueryCore <TResult>(query, _queryModelGenerator, _database, _logger, _contextType));

                return(compiledQuery(queryContext));
            }
        }