/// <summary> /// Refer to CachingQuerySqlBuilder.MutateResultToMatchCurrentQuery /// </summary> internal static QueryResult MutateResultToMatchCurrentQuery(CachingQueryRunnerValue cacheValue, StructuredQuery currentQuery) { if (cacheValue == null) { throw new ArgumentNullException("cacheValue"); } if (currentQuery == null) { throw new ArgumentNullException("currentQuery"); } // Clone result QueryBuild queryBuild = CachingQuerySqlBuilder.MutateResultToMatchCurrentQuery(cacheValue.QueryResult.QueryBuild, cacheValue.OriginalQuery, currentQuery); QueryResult result = cacheValue.QueryResult.ShallowClone(queryBuild); return(result); }
/// <summary> /// Try to get the value from cache, with logging. /// </summary> private bool TryGetOrAdd(CachingQueryRunnerKey key, MessageContext msg, out CachingQueryRunnerValue result, Func <CachingQueryRunnerKey, CachingQueryRunnerValue> valueFactory) { bool foundValue; foundValue = Cache.TryGetOrAdd(key, out result, valueFactory); msg.Append(() => "CachingQueryRunner key:" + key); if (foundValue) { var cacheValue = result; msg.Append(() => "CachingQueryRunner cache hit"); msg.Append(() => $"Entry originally cached at {cacheValue.CacheTime}"); } else { msg.Append(() => "CachingQueryRunner cache miss"); } return(foundValue); }
/// <summary> /// Build the SQL, or collect it from cache. /// </summary> /// <param name="query"></param> /// <param name="settings"></param> /// <returns></returns> public QueryResult ExecuteQuery(StructuredQuery query, QuerySettings settings) { // Validate if (query == null) { throw new ArgumentNullException("query"); } if (QueryRunner == null) { throw new InvalidOperationException("QueryRunner not set."); } if (settings == null) { settings = new QuerySettings( ); } // Determine if we should cache .. and the cache key QueryBuild builtQuery; CachingQueryRunnerKey key; CacheContext queryBuilderCacheContext; using (queryBuilderCacheContext = new CacheContext()) { key = CreateCacheKeyAndQuery(query, settings, out builtQuery); } // A null key means that the ersult should not participate in caching if (key == null) { return(RunQueryImpl(query, settings, builtQuery)); } CachingQueryRunnerValue cacheValue; using (MessageContext msg = new MessageContext("Reports")) { // Check for force recalculation if (settings.RefreshCachedResult) { msg.Append(() => "CachingQueryRunner refreshed forced"); Cache.Remove(key); } // Run query bool fromCache = TryGetOrAdd(key, msg, out cacheValue, callbackKey => { using (CacheContext cacheContext = new CacheContext( )) { QueryResult queryResult = RunQueryImpl(query, settings, builtQuery); cacheValue = new CachingQueryRunnerValue(query, queryResult); // Add the cache context entries to the appropriate CacheInvalidator _cacheInvalidator.AddInvalidations(cacheContext, callbackKey); _cacheInvalidator.AddInvalidations(queryBuilderCacheContext, callbackKey); } return(cacheValue); }); if (fromCache && CacheContext.IsSet()) { using (CacheContext cacheContext = CacheContext.GetContext( )) { // Add the already stored changes that should invalidate this cache // entry to any outer or containing cache contexts. cacheContext.AddInvalidationsFor(_cacheInvalidator, key); } } } if (cacheValue == null) { throw new Exception("Assert false"); } // Mutate returned result to be suitable for current query QueryResult result; if (cacheValue.OriginalQuery == query) { result = cacheValue.QueryResult; } else { result = MutateResultToMatchCurrentQuery(cacheValue, query); } return(result); }