Пример #1
0
        /// <summary>
        /// Sets the file watch expiration.
        /// </summary>
        /// <param name="options">The options.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>MemoryCacheEntryOptions.</returns>
        public static MemoryCacheEntryOptions SetFileWatchExpiration(this MemoryCacheEntryOptions options, string fileName)
        {
            var fileInfo     = new FileInfo(fileName);
            var fileProvider = new PhysicalFileProvider(fileInfo.DirectoryName);

            options.AddExpirationToken(fileProvider.Watch(fileInfo.Name));
            return(options);
        }
Пример #2
0
        public void GetCurrentStatistics_UpdateAfterExistingItemExpired_CurrentEstimatedSizeResets(bool sizeLimitIsSet)
        {
            const string Key = "myKey";

            var cache = new MemoryCache(sizeLimitIsSet ?
                                        new MemoryCacheOptions {
                TrackStatistics = true, Clock = new SystemClock(), SizeLimit = 10
            } :
                                        new MemoryCacheOptions {
                TrackStatistics = true, Clock = new SystemClock()
            }
                                        );

            ICacheEntry entry;

            using (entry = cache.CreateEntry(Key))
            {
                var expirationToken = new TestExpirationToken()
                {
                    ActiveChangeCallbacks = true
                };
                var mc = new MemoryCacheEntryOptions {
                    Size = 5
                };
                cache.Set(Key, new object(), mc.AddExpirationToken(expirationToken));
                MemoryCacheStatistics?stats = cache.GetCurrentStatistics();

                Assert.NotNull(stats);
                Assert.Equal(1, cache.Count);
                Assert.Equal(1, stats.CurrentEntryCount);
                VerifyCurrentEstimatedSize(5, sizeLimitIsSet, stats);

                expirationToken.HasChanged = true;
                cache.Set(Key, new object(), mc.AddExpirationToken(expirationToken));
                stats = cache.GetCurrentStatistics();

                Assert.NotNull(stats);
                Assert.Equal(0, cache.Count);
                Assert.Equal(0, stats.CurrentEntryCount);
                VerifyCurrentEstimatedSize(0, sizeLimitIsSet, stats);
            }
        }
Пример #3
0
        /// <summary>
        /// Adds version query parameter to the specified file path.
        /// </summary>
        /// <param name="path">The path of the file to which version should be added.</param>
        /// <returns>Path containing the version query string.</returns>
        /// <remarks>
        /// The version query string is appended with the key "v".
        /// </remarks>
        public string AddFileVersionToPath(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            var resolvedPath = path;

            var queryStringOrFragmentStartIndex = path.IndexOfAny(QueryStringAndFragmentTokens);
            if (queryStringOrFragmentStartIndex != -1)
            {
                resolvedPath = path.Substring(0, queryStringOrFragmentStartIndex);
            }

            Uri uri;
            if (Uri.TryCreate(resolvedPath, UriKind.Absolute, out uri) && !uri.IsFile)
            {
                // Don't append version if the path is absolute.
                return path;
            }

            string value;
            if (!_cache.TryGetValue(path, out value))
            {
                var cacheEntryOptions = new MemoryCacheEntryOptions();
                cacheEntryOptions.AddExpirationToken(_fileProvider.Watch(resolvedPath));
                var fileInfo = _fileProvider.GetFileInfo(resolvedPath);

                if (!fileInfo.Exists &&
                    _requestPathBase.HasValue &&
                    resolvedPath.StartsWith(_requestPathBase.Value, StringComparison.OrdinalIgnoreCase))
                {
                    var requestPathBaseRelativePath = resolvedPath.Substring(_requestPathBase.Value.Length);
                    cacheEntryOptions.AddExpirationToken(_fileProvider.Watch(requestPathBaseRelativePath));
                    fileInfo = _fileProvider.GetFileInfo(requestPathBaseRelativePath);
                }

                if (fileInfo.Exists)
                {
                    value = QueryHelpers.AddQueryString(path, VersionKey, GetHashForFile(fileInfo));
                }
                else
                {
                    // if the file is not in the current server.
                    value = path;
                }

                value = _cache.Set<string>(path, value, cacheEntryOptions);
            }

            return value;
        }
Пример #4
0
        /// <summary>
        /// Adds inherited token and absolute expiration information.
        /// </summary>
        /// <param name="link"></param>
        public static MemoryCacheEntryOptions AddEntryLink(this MemoryCacheEntryOptions options, IEntryLink link)
        {
            foreach (var expirationToken in link.ExpirationTokens)
            {
                options.AddExpirationToken(expirationToken);
            }

            if (link.AbsoluteExpiration.HasValue)
            {
                options.SetAbsoluteExpiration(link.AbsoluteExpiration.Value);
            }
            return(options);
        }
Пример #5
0
        private ViewLocationCacheResult OnCacheMiss(
            ViewLocationExpanderContext expanderContext,
            ViewLocationCacheKey cacheKey)
        {
            // Only use the area view location formats if we have an area token.
            IEnumerable<string> viewLocations = !string.IsNullOrEmpty(expanderContext.AreaName) ?
                _options.AreaViewLocationFormats :
                _options.ViewLocationFormats;

            for (var i = 0; i < _options.ViewLocationExpanders.Count; i++)
            {
                viewLocations = _options.ViewLocationExpanders[i].ExpandViewLocations(expanderContext, viewLocations);
            }

            ViewLocationCacheResult cacheResult = null;
            var searchedLocations = new List<string>();
            var expirationTokens = new HashSet<IChangeToken>();
            foreach (var location in viewLocations)
            {
                var path = string.Format(
                    CultureInfo.InvariantCulture,
                    location,
                    expanderContext.ViewName,
                    expanderContext.ControllerName,
                    expanderContext.AreaName);

                cacheResult = CreateCacheResult(expirationTokens, path, expanderContext.IsMainPage);
                if (cacheResult != null)
                {
                    break;
                }

                searchedLocations.Add(path);
            }

            // No views were found at the specified location. Create a not found result.
            if (cacheResult == null)
            {
                cacheResult = new ViewLocationCacheResult(searchedLocations);
            }

            var cacheEntryOptions = new MemoryCacheEntryOptions();
            cacheEntryOptions.SetSlidingExpiration(_cacheExpirationDuration);
            foreach (var expirationToken in expirationTokens)
            {
                cacheEntryOptions.AddExpirationToken(expirationToken);
            }

            return ViewLookupCache.Set<ViewLocationCacheResult>(cacheKey, cacheResult, cacheEntryOptions);
        }
Пример #6
0
        private ViewLocationCacheResult LocatePageFromPath(string executingFilePath, string pagePath, bool isMainPage)
        {
            var applicationRelativePath = GetAbsolutePath(executingFilePath, pagePath);
            var cacheKey = new ViewLocationCacheKey(applicationRelativePath, isMainPage);
            ViewLocationCacheResult cacheResult;
            if (!ViewLookupCache.TryGetValue(cacheKey, out cacheResult))
            {
                var expirationTokens = new HashSet<IChangeToken>();
                cacheResult = CreateCacheResult(expirationTokens, applicationRelativePath, isMainPage);

                var cacheEntryOptions = new MemoryCacheEntryOptions();
                cacheEntryOptions.SetSlidingExpiration(_cacheExpirationDuration);
                foreach (var expirationToken in expirationTokens)
                {
                    cacheEntryOptions.AddExpirationToken(expirationToken);
                }

                // No views were found at the specified location. Create a not found result.
                if (cacheResult == null)
                {
                    cacheResult = new ViewLocationCacheResult(new[] { applicationRelativePath });
                }

                cacheResult = ViewLookupCache.Set<ViewLocationCacheResult>(
                    cacheKey,
                    cacheResult,
                    cacheEntryOptions);
            }

            return cacheResult;
        }
Пример #7
0
        private Task<CompilerCacheResult> CreateCacheEntry(
            string normalizedPath,
            Func<RelativeFileInfo, CompilationResult> compile)
        {
            TaskCompletionSource<CompilerCacheResult> compilationTaskSource = null;
            MemoryCacheEntryOptions cacheEntryOptions = null;
            IFileInfo fileInfo = null;
            Task<CompilerCacheResult> cacheEntry;

            // Safe races cannot be allowed when compiling Razor pages. To ensure only one compilation request succeeds
            // per file, we'll lock the creation of a cache entry. Creating the cache entry should be very quick. The
            // actual work for compiling files happens outside the critical section.
            lock (_cacheLock)
            {
                if (_cache.TryGetValue(normalizedPath, out cacheEntry))
                {
                    return cacheEntry;
                }

                fileInfo = _fileProvider.GetFileInfo(normalizedPath);
                if (!fileInfo.Exists)
                {
                    var expirationToken = _fileProvider.Watch(normalizedPath);
                    cacheEntry = Task.FromResult(new CompilerCacheResult(new[] { expirationToken }));

                    cacheEntryOptions = new MemoryCacheEntryOptions();
                    cacheEntryOptions.AddExpirationToken(expirationToken);
                }
                else
                {
                    cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath);

                    // A file exists and needs to be compiled.
                    compilationTaskSource = new TaskCompletionSource<CompilerCacheResult>();
                    cacheEntry = compilationTaskSource.Task;
                }

                cacheEntry = _cache.Set<Task<CompilerCacheResult>>(normalizedPath, cacheEntry, cacheEntryOptions);
            }

            if (compilationTaskSource != null)
            {
                // Indicates that the file was found and needs to be compiled.
                Debug.Assert(fileInfo != null && fileInfo.Exists);
                Debug.Assert(cacheEntryOptions != null);
                var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath);

                try
                {
                    var compilationResult = compile(relativeFileInfo);
                    compilationResult.EnsureSuccessful();
                    compilationTaskSource.SetResult(
                        new CompilerCacheResult(compilationResult, cacheEntryOptions.ExpirationTokens));
                }
                catch (Exception ex)
                {
                    compilationTaskSource.SetException(ex);
                }
            }

            return cacheEntry;
        }
Пример #8
0
        private MemoryCacheEntryOptions GetMemoryCacheEntryOptions(string relativePath)
        {
            var options = new MemoryCacheEntryOptions();
            options.AddExpirationToken(_fileProvider.Watch(relativePath));

            var viewImportsPaths = ViewHierarchyUtility.GetViewImportsLocations(relativePath);
            foreach (var location in viewImportsPaths)
            {
                options.AddExpirationToken(_fileProvider.Watch(location));
            }

            return options;
        }
Пример #9
0
        private CompilerCacheResult CreateCacheEntry(
            string normalizedPath,
            Func<RelativeFileInfo, CompilationResult> compile)
        {
            CompilerCacheResult cacheResult;
            var fileInfo = _fileProvider.GetFileInfo(normalizedPath);
            MemoryCacheEntryOptions cacheEntryOptions;
            CompilerCacheResult cacheResultToCache;
            if (!fileInfo.Exists)
            {
                cacheResultToCache = CompilerCacheResult.FileNotFound;
                cacheResult = CompilerCacheResult.FileNotFound;

                cacheEntryOptions = new MemoryCacheEntryOptions();
                cacheEntryOptions.AddExpirationToken(_fileProvider.Watch(normalizedPath));
            }
            else
            {
                var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath);
                var compilationResult = compile(relativeFileInfo).EnsureSuccessful();
                cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath);

                // By default the CompilationResult returned by IRoslynCompiler is an instance of
                // UncachedCompilationResult. This type has the generated code as a string property and do not want
                // to cache it. We'll instead cache the unwrapped result.
                cacheResultToCache = new CompilerCacheResult(
                    CompilationResult.Successful(compilationResult.CompiledType));
                cacheResult = new CompilerCacheResult(compilationResult);
            }

            _cache.Set(normalizedPath, cacheResultToCache, cacheEntryOptions);
            return cacheResult;
        }
Пример #10
0
 /// <summary>
 /// Sets the cache entry change expiration.
 /// </summary>
 /// <param name="options">The options.</param>
 /// <param name="cache">The cache.</param>
 /// <param name="key">The key.</param>
 /// <returns>MemoryCacheEntryOptions.</returns>
 public static MemoryCacheEntryOptions SetCacheEntryChangeExpiration(this MemoryCacheEntryOptions options, IMemoryCache cache, string key)
 {
     options.AddExpirationToken(MakeCacheEntryChangeToken(cache, new[] { key }));
     return(options);
 }
Пример #11
0
 /// <summary>
 /// Forces the absolute expiration.
 /// </summary>
 /// <param name="options">The options.</param>
 /// <param name="delay">The delay.</param>
 /// <returns>MemoryCacheEntryOptions.</returns>
 public static MemoryCacheEntryOptions ForceAbsoluteExpiration(this MemoryCacheEntryOptions options, TimeSpan delay) =>
 options.AddExpirationToken(new CancellationChangeToken(new CancellationTokenSource(delay).Token));
Пример #12
0
        private static IMemoryCache MakeCache(object result = null)
        {
            var cache = new Mock<IMemoryCache>();
            cache.CallBase = true;
            cache.Setup(c => c.TryGetValue(It.IsAny<string>(), out result))
                .Returns(result != null);

            var cacheEntryOptions = new MemoryCacheEntryOptions();
            cacheEntryOptions.AddExpirationToken(Mock.Of<IChangeToken>());
            cache
                .Setup(
                    c => c.Set(
                        /*key*/ It.IsAny<string>(),
                        /*value*/ It.IsAny<object>(),
                        /*options*/ cacheEntryOptions))
                .Returns(result);
            return cache.Object;
        }