React to Caching api review changes
This commit is contained in:
parent
8a701726b3
commit
2a321fd622
|
|
@ -1,9 +1,11 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.Framework.Caching;
|
using Microsoft.Framework.Caching;
|
||||||
using Microsoft.Framework.Caching.Memory;
|
using Microsoft.Framework.Caching.Memory;
|
||||||
|
using TagHelperSample.Web.Models;
|
||||||
using TagHelperSample.Web.Services;
|
using TagHelperSample.Web.Services;
|
||||||
|
|
||||||
namespace TagHelperSample.Web.Components
|
namespace TagHelperSample.Web.Components
|
||||||
|
|
@ -11,31 +13,41 @@ namespace TagHelperSample.Web.Components
|
||||||
[ViewComponent(Name = "FeaturedMovies")]
|
[ViewComponent(Name = "FeaturedMovies")]
|
||||||
public class FeaturedMoviesComponent : ViewComponent
|
public class FeaturedMoviesComponent : ViewComponent
|
||||||
{
|
{
|
||||||
private MoviesService _moviesService;
|
private readonly IMemoryCache _cache;
|
||||||
|
private readonly MoviesService _moviesService;
|
||||||
|
|
||||||
public FeaturedMoviesComponent(MoviesService moviesService)
|
public FeaturedMoviesComponent(MoviesService moviesService, IMemoryCache cache)
|
||||||
{
|
{
|
||||||
_moviesService = moviesService;
|
_moviesService = moviesService;
|
||||||
|
_cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke()
|
public IViewComponentResult Invoke()
|
||||||
{
|
{
|
||||||
IExpirationTrigger trigger;
|
// Since this component is invoked from within a CacheTagHelper,
|
||||||
var movies = _moviesService.GetFeaturedMovies(out trigger);
|
// cache the movie list and provide an expiration trigger, which when triggered causes the
|
||||||
|
// CacheTagHelper's cached data to be invalidated.
|
||||||
// Add custom triggers
|
var cacheKey = "featured_movies";
|
||||||
EntryLinkHelpers.ContextLink.AddExpirationTriggers(new[] { trigger });
|
IEnumerable<FeaturedMovies> movies;
|
||||||
|
if (!_cache.TryGetValue(cacheKey, out movies))
|
||||||
|
{
|
||||||
|
IExpirationTrigger trigger;
|
||||||
|
movies = _moviesService.GetFeaturedMovies(out trigger);
|
||||||
|
_cache.Set(cacheKey, movies, new MemoryCacheEntryOptions().AddExpirationTrigger(trigger));
|
||||||
|
}
|
||||||
|
|
||||||
return View(movies);
|
return View(movies);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string movieName)
|
public IViewComponentResult Invoke(string movieName)
|
||||||
{
|
{
|
||||||
IExpirationTrigger trigger;
|
string quote;
|
||||||
var quote = _moviesService.GetCriticsQuote(out trigger);
|
if (!_cache.TryGetValue(movieName, out quote))
|
||||||
|
{
|
||||||
// This is invoked as part of a nested cache tag helper.
|
IExpirationTrigger trigger;
|
||||||
EntryLinkHelpers.ContextLink.AddExpirationTriggers(new[] { trigger });
|
quote = _moviesService.GetCriticsQuote(out trigger);
|
||||||
|
_cache.Set(movieName, quote, new MemoryCacheEntryOptions().AddExpirationTrigger(trigger));
|
||||||
|
}
|
||||||
|
|
||||||
return Content(quote);
|
return Content(quote);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<cache expires-after="TimeSpan.FromMinutes(20)">
|
<cache expires-after="TimeSpan.FromMinutes(20)">
|
||||||
@await Component.InvokeAsync("FeaturedMovies")
|
@Component.Invoke("FeaturedMovies")
|
||||||
</cache>
|
</cache>
|
||||||
</div>
|
</div>
|
||||||
<div style="clear: left"></div>
|
<div style="clear: left"></div>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
||||||
{
|
{
|
||||||
private static readonly MemoryCacheOptions MemoryCacheOptions = new MemoryCacheOptions
|
private static readonly MemoryCacheOptions MemoryCacheOptions = new MemoryCacheOptions
|
||||||
{
|
{
|
||||||
ListenForMemoryPressure = false
|
CompactOnMemoryPressure = false
|
||||||
};
|
};
|
||||||
private static readonly TimeSpan SlidingExpirationDuration = TimeSpan.FromMinutes(1);
|
private static readonly TimeSpan SlidingExpirationDuration = TimeSpan.FromMinutes(1);
|
||||||
private readonly IFileProvider _fileProvider;
|
private readonly IFileProvider _fileProvider;
|
||||||
|
|
@ -43,22 +43,25 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
|
||||||
public CodeTree GetOrAdd([NotNull] string pagePath,
|
public CodeTree GetOrAdd([NotNull] string pagePath,
|
||||||
[NotNull] Func<IFileInfo, CodeTree> getCodeTree)
|
[NotNull] Func<IFileInfo, CodeTree> getCodeTree)
|
||||||
{
|
{
|
||||||
return _codeTreeCache.GetOrSet(pagePath, getCodeTree, OnCacheMiss);
|
CodeTree codeTree;
|
||||||
}
|
if (!_codeTreeCache.TryGetValue(pagePath, out codeTree))
|
||||||
|
{
|
||||||
|
// GetOrAdd is invoked for each _GlobalImport that might potentially exist in the path.
|
||||||
|
// We can avoid performing file system lookups for files that do not exist by caching
|
||||||
|
// negative results and adding a Watch for that file.
|
||||||
|
|
||||||
private CodeTree OnCacheMiss(ICacheSetContext cacheSetContext)
|
var options = new MemoryCacheEntryOptions()
|
||||||
{
|
.AddExpirationTrigger(_fileProvider.Watch(pagePath))
|
||||||
var pagePath = cacheSetContext.Key;
|
.SetSlidingExpiration(SlidingExpirationDuration);
|
||||||
var getCodeTree = (Func<IFileInfo, CodeTree>)cacheSetContext.State;
|
|
||||||
|
|
||||||
// GetOrAdd is invoked for each _ViewImports that might potentially exist in the path.
|
var file = _fileProvider.GetFileInfo(pagePath);
|
||||||
// We can avoid performing file system lookups for files that do not exist by caching
|
codeTree = file.Exists ? getCodeTree(file) : null;
|
||||||
// negative results and adding a Watch for that file.
|
|
||||||
cacheSetContext.AddExpirationTrigger(_fileProvider.Watch(pagePath));
|
|
||||||
cacheSetContext.SetSlidingExpiration(SlidingExpirationDuration);
|
|
||||||
|
|
||||||
var file = _fileProvider.GetFileInfo(pagePath);
|
|
||||||
return file.Exists ? getCodeTree(file) : null;
|
_codeTreeCache.Set(pagePath, codeTree, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return codeTree;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +45,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
IFileProvider fileProvider)
|
IFileProvider fileProvider)
|
||||||
{
|
{
|
||||||
_fileProvider = fileProvider;
|
_fileProvider = fileProvider;
|
||||||
_cache = new MemoryCache(new MemoryCacheOptions { ListenForMemoryPressure = false });
|
_cache = new MemoryCache(new MemoryCacheOptions { CompactOnMemoryPressure = false });
|
||||||
|
|
||||||
var cacheEntries = new List<CompilerCacheEntry>();
|
var cacheEntries = new List<CompilerCacheEntry>();
|
||||||
foreach (var viewCollection in razorFileInfoCollections)
|
foreach (var viewCollection in razorFileInfoCollections)
|
||||||
|
|
@ -58,7 +58,11 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
|
|
||||||
// There shouldn't be any duplicates and if there are any the first will win.
|
// There shouldn't be any duplicates and if there are any the first will win.
|
||||||
// If the result doesn't match the one on disk its going to recompile anyways.
|
// If the result doesn't match the one on disk its going to recompile anyways.
|
||||||
_cache.Set(NormalizePath(fileInfo.RelativePath), cacheEntry, PopulateCacheSetContext);
|
var normalizedPath = NormalizePath(fileInfo.RelativePath);
|
||||||
|
_cache.Set(
|
||||||
|
normalizedPath,
|
||||||
|
cacheEntry,
|
||||||
|
GetMemoryCacheEntryOptions(fileInfo.RelativePath));
|
||||||
|
|
||||||
cacheEntries.Add(cacheEntry);
|
cacheEntries.Add(cacheEntry);
|
||||||
}
|
}
|
||||||
|
|
@ -184,9 +188,12 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var compilationResult = compile(file).EnsureSuccessful();
|
var compilationResult = compile(file).EnsureSuccessful();
|
||||||
|
|
||||||
// Concurrent addition to MemoryCache with the same key result in safe race.
|
// Concurrent addition to MemoryCache with the same key result in safe race.
|
||||||
var cacheEntry = _cache.Set(normalizedPath,
|
var compilerCacheEntry = new CompilerCacheEntry(file, compilationResult.CompiledType);
|
||||||
new CompilerCacheEntry(file, compilationResult.CompiledType),
|
var cacheEntry = _cache.Set<CompilerCacheEntry>(
|
||||||
PopulateCacheSetContext);
|
normalizedPath,
|
||||||
|
compilerCacheEntry,
|
||||||
|
GetMemoryCacheEntryOptions(compilerCacheEntry.RelativePath));
|
||||||
|
|
||||||
return new GetOrAddResult
|
return new GetOrAddResult
|
||||||
{
|
{
|
||||||
CompilationResult = compilationResult,
|
CompilationResult = compilationResult,
|
||||||
|
|
@ -194,18 +201,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompilerCacheEntry PopulateCacheSetContext(ICacheSetContext cacheSetContext)
|
private MemoryCacheEntryOptions GetMemoryCacheEntryOptions(string relativePath)
|
||||||
{
|
{
|
||||||
var entry = (CompilerCacheEntry)cacheSetContext.State;
|
var options = new MemoryCacheEntryOptions();
|
||||||
cacheSetContext.AddExpirationTrigger(_fileProvider.Watch(entry.RelativePath));
|
options.AddExpirationTrigger(_fileProvider.Watch(relativePath));
|
||||||
|
|
||||||
var viewImportsPaths = ViewHierarchyUtility.GetViewImportsLocations(cacheSetContext.Key);
|
var viewImportsPaths = ViewHierarchyUtility.GetViewImportsLocations(relativePath);
|
||||||
foreach (var location in viewImportsPaths)
|
foreach (var location in viewImportsPaths)
|
||||||
{
|
{
|
||||||
cacheSetContext.AddExpirationTrigger(_fileProvider.Watch(location));
|
options.AddExpirationTrigger(_fileProvider.Watch(location));
|
||||||
}
|
}
|
||||||
|
return options;
|
||||||
return entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AssociatedGlobalFilesChanged(CompilerCacheEntry entry,
|
private bool AssociatedGlobalFilesChanged(CompilerCacheEntry entry,
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
||||||
Parallel.For(0, filesToProcess.Count, parallelOptions, index =>
|
Parallel.For(0, filesToProcess.Count, parallelOptions, index =>
|
||||||
{
|
{
|
||||||
var file = filesToProcess[index];
|
var file = filesToProcess[index];
|
||||||
var cacheEntry = PreCompilationCache.GetOrSet(file.RelativePath,
|
|
||||||
file,
|
PrecompilationCacheEntry cacheEntry;
|
||||||
OnCacheMiss);
|
if(!PreCompilationCache.TryGetValue(file.RelativePath, out cacheEntry))
|
||||||
|
{
|
||||||
|
cacheEntry = GetCacheEntry(file);
|
||||||
|
PreCompilationCache.Set(
|
||||||
|
file.RelativePath,
|
||||||
|
cacheEntry,
|
||||||
|
GetMemoryCacheEntryOptions(file, cacheEntry));
|
||||||
|
}
|
||||||
|
|
||||||
if (cacheEntry != null)
|
if (cacheEntry != null)
|
||||||
{
|
{
|
||||||
if (cacheEntry.Success)
|
if (cacheEntry.Success)
|
||||||
|
|
@ -190,21 +198,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private PrecompilationCacheEntry OnCacheMiss(ICacheSetContext cacheSetContext)
|
private MemoryCacheEntryOptions GetMemoryCacheEntryOptions(
|
||||||
|
RelativeFileInfo fileInfo,
|
||||||
|
PrecompilationCacheEntry cacheEntry)
|
||||||
{
|
{
|
||||||
var fileInfo = (RelativeFileInfo)cacheSetContext.State;
|
var options = new MemoryCacheEntryOptions();
|
||||||
var entry = GetCacheEntry(fileInfo);
|
options.AddExpirationTrigger(FileProvider.Watch(fileInfo.RelativePath));
|
||||||
|
foreach (var path in ViewHierarchyUtility.GetViewImportsLocations(fileInfo.RelativePath))
|
||||||
if (entry != null)
|
|
||||||
{
|
{
|
||||||
cacheSetContext.AddExpirationTrigger(FileProvider.Watch(fileInfo.RelativePath));
|
options.AddExpirationTrigger(FileProvider.Watch(path));
|
||||||
foreach (var path in ViewHierarchyUtility.GetViewImportsLocations(fileInfo.RelativePath))
|
|
||||||
{
|
|
||||||
cacheSetContext.AddExpirationTrigger(FileProvider.Watch(path));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return options;
|
||||||
return entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetFileInfosRecursive(string root, List<RelativeFileInfo> razorFiles)
|
private void GetFileInfosRecursive(string root, List<RelativeFileInfo> razorFiles)
|
||||||
|
|
|
||||||
|
|
@ -106,10 +106,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
public TimeSpan? ExpiresSliding { get; set; }
|
public TimeSpan? ExpiresSliding { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="CachePreservationPriority"/> policy for the cache entry.
|
/// Gets or sets the <see cref="CacheItemPriority"/> policy for the cache entry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HtmlAttributeName(CachePriorityAttributeName)]
|
[HtmlAttributeName(CachePriorityAttributeName)]
|
||||||
public CachePreservationPriority? Priority { get; set; }
|
public CacheItemPriority? Priority { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the value which determines if the tag helper is enabled or not.
|
/// Gets or sets the value which determines if the tag helper is enabled or not.
|
||||||
|
|
@ -126,19 +126,14 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
var key = GenerateKey(context);
|
var key = GenerateKey(context);
|
||||||
if (!MemoryCache.TryGetValue(key, out result))
|
if (!MemoryCache.TryGetValue(key, out result))
|
||||||
{
|
{
|
||||||
// Create an EntryLink and flow it so that it is accessible via the ambient
|
// Create an entry link scope and flow it so that any triggers related to the cache entries
|
||||||
// EntryLinkHelpers.ContextLink for user code.
|
// created within this scope get copied to this scope.
|
||||||
var entryLink = new EntryLink();
|
using (var link = MemoryCache.CreateLinkingScope())
|
||||||
using (entryLink.FlowContext())
|
|
||||||
{
|
{
|
||||||
result = await context.GetChildContentAsync();
|
result = await context.GetChildContentAsync();
|
||||||
}
|
|
||||||
|
|
||||||
MemoryCache.Set(key, cacheSetContext =>
|
MemoryCache.Set(key, result, GetMemoryCacheEntryOptions(link));
|
||||||
{
|
}
|
||||||
UpdateCacheContext(cacheSetContext, entryLink);
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,29 +192,31 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for unit testing
|
// Internal for unit testing
|
||||||
internal void UpdateCacheContext(ICacheSetContext cacheSetContext, EntryLink entryLink)
|
internal MemoryCacheEntryOptions GetMemoryCacheEntryOptions(IEntryLink entryLink)
|
||||||
{
|
{
|
||||||
|
var options = new MemoryCacheEntryOptions();
|
||||||
if (ExpiresOn != null)
|
if (ExpiresOn != null)
|
||||||
{
|
{
|
||||||
cacheSetContext.SetAbsoluteExpiration(ExpiresOn.Value);
|
options.SetAbsoluteExpiration(ExpiresOn.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ExpiresAfter != null)
|
if (ExpiresAfter != null)
|
||||||
{
|
{
|
||||||
cacheSetContext.SetAbsoluteExpiration(ExpiresAfter.Value);
|
options.SetAbsoluteExpiration(ExpiresAfter.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ExpiresSliding != null)
|
if (ExpiresSliding != null)
|
||||||
{
|
{
|
||||||
cacheSetContext.SetSlidingExpiration(ExpiresSliding.Value);
|
options.SetSlidingExpiration(ExpiresSliding.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Priority != null)
|
if (Priority != null)
|
||||||
{
|
{
|
||||||
cacheSetContext.SetPriority(Priority.Value);
|
options.SetPriority(Priority.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheSetContext.AddEntryLink(entryLink);
|
options.AddEntryLink(entryLink);
|
||||||
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddStringCollectionKey(StringBuilder builder,
|
private static void AddStringCollectionKey(StringBuilder builder,
|
||||||
|
|
|
||||||
|
|
@ -65,12 +65,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _cache.GetOrSet(path, cacheGetOrSetContext =>
|
string value;
|
||||||
|
if(!_cache.TryGetValue(path, out value))
|
||||||
{
|
{
|
||||||
var trigger = _fileProvider.Watch(resolvedPath);
|
value = QueryHelpers.AddQueryString(path, VersionKey, GetHashForFile(fileInfo));
|
||||||
cacheGetOrSetContext.AddExpirationTrigger(trigger);
|
_cache.Set(
|
||||||
return QueryHelpers.AddQueryString(path, VersionKey, GetHashForFile(fileInfo));
|
path,
|
||||||
});
|
value,
|
||||||
|
new MemoryCacheEntryOptions().AddExpirationTrigger(_fileProvider.Watch(resolvedPath)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetHashForFile(IFileInfo fileInfo)
|
private string GetHashForFile(IFileInfo fileInfo)
|
||||||
|
|
|
||||||
|
|
@ -101,16 +101,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
if (Cache != null)
|
if (Cache != null)
|
||||||
{
|
{
|
||||||
var cacheKey = $"{nameof(GlobbingUrlBuilder)}-inc:{include}-exc:{exclude}";
|
var cacheKey = $"{nameof(GlobbingUrlBuilder)}-inc:{include}-exc:{exclude}";
|
||||||
return Cache.GetOrSet(cacheKey, cacheSetContext =>
|
IEnumerable<string> files;
|
||||||
|
if (!Cache.TryGetValue(cacheKey, out files))
|
||||||
{
|
{
|
||||||
|
var options = new MemoryCacheEntryOptions();
|
||||||
foreach (var pattern in includePatterns)
|
foreach (var pattern in includePatterns)
|
||||||
{
|
{
|
||||||
var trigger = FileProvider.Watch(pattern);
|
var trigger = FileProvider.Watch(pattern);
|
||||||
cacheSetContext.AddExpirationTrigger(trigger);
|
options.AddExpirationTrigger(trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FindFiles(includePatterns, excludePatterns);
|
files = FindFiles(includePatterns, excludePatterns);
|
||||||
});
|
|
||||||
|
Cache.Set(cacheKey, files, options);
|
||||||
|
}
|
||||||
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FindFiles(includePatterns, excludePatterns);
|
return FindFiles(includePatterns, excludePatterns);
|
||||||
|
|
@ -175,7 +180,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
// Only extension differs so just compare the extension
|
// Only extension differs so just compare the extension
|
||||||
var xExt = xExtIndex >= 0 ? x.Substring(xExtIndex) : string.Empty;
|
var xExt = xExtIndex >= 0 ? x.Substring(xExtIndex) : string.Empty;
|
||||||
var yExt = yExtIndex >= 0 ? y.Substring(yExtIndex) : string.Empty;
|
var yExt = yExtIndex >= 0 ? y.Substring(yExtIndex) : string.Empty;
|
||||||
|
|
||||||
return string.Compare(xExt, yExt, StringComparison.Ordinal);
|
return string.Compare(xExt, yExt, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,10 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
_appServices = services;
|
_appServices = services;
|
||||||
|
|
||||||
// When ListenForMemoryPressure is true, the MemoryCache evicts items at every gen2 collection.
|
// When CompactOnMemoryPressure is true, the MemoryCache evicts items at every gen2 collection.
|
||||||
// In DTH, gen2 happens frequently enough to make it undesirable for caching precompilation results. We'll
|
// In DTH, gen2 happens frequently enough to make it undesirable for caching precompilation results. We'll
|
||||||
// disable listening for memory pressure for the MemoryCache instance used by precompilation.
|
// disable listening for memory pressure for the MemoryCache instance used by precompilation.
|
||||||
_memoryCache = new MemoryCache(new MemoryCacheOptions { ListenForMemoryPressure = false });
|
_memoryCache = new MemoryCache(new MemoryCacheOptions { CompactOnMemoryPressure = false });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,10 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using Microsoft.AspNet.Mvc.Razor.Directives;
|
using Microsoft.AspNet.Mvc.Razor.Directives;
|
||||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||||
using Microsoft.Framework.Caching.Memory;
|
using Microsoft.Framework.Caching.Memory;
|
||||||
using Microsoft.Framework.Caching.Memory.Infrastructure;
|
using Microsoft.Framework.Internal;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -16,7 +17,7 @@ using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.Framework.Caching;
|
using Microsoft.Framework.Caching;
|
||||||
using Microsoft.Framework.Caching.Memory;
|
using Microsoft.Framework.Caching.Memory;
|
||||||
using Microsoft.Framework.Caching.Memory.Infrastructure;
|
using Microsoft.Framework.Internal;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -244,15 +245,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
var childContent = "original-child-content";
|
var childContent = "original-child-content";
|
||||||
var cache = new Mock<IMemoryCache>();
|
var cache = new Mock<IMemoryCache>();
|
||||||
cache.CallBase = true;
|
cache.CallBase = true;
|
||||||
|
var value = new DefaultTagHelperContent().SetContent("ok");
|
||||||
cache.Setup(c => c.Set(
|
cache.Setup(c => c.Set(
|
||||||
/*key*/ It.IsAny<string>(),
|
/*key*/ It.IsAny<string>(),
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ value,
|
||||||
/*state*/ It.IsAny<object>(),
|
/*optons*/ It.IsAny<MemoryCacheEntryOptions>()))
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
|
.Returns(value)
|
||||||
.Returns(new DefaultTagHelperContent().SetContent("ok"))
|
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
object cacheResult;
|
object cacheResult;
|
||||||
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out cacheResult))
|
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), out cacheResult))
|
||||||
.Returns(false);
|
.Returns(false);
|
||||||
var tagHelperContext = GetTagHelperContext(id, childContent);
|
var tagHelperContext = GetTagHelperContext(id, childContent);
|
||||||
var tagHelperOutput = new TagHelperOutput("cache", new TagHelperAttributeList());
|
var tagHelperOutput = new TagHelperOutput("cache", new TagHelperAttributeList());
|
||||||
|
|
@ -270,9 +271,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
Assert.Equal(childContent, tagHelperOutput.Content.GetContent());
|
Assert.Equal(childContent, tagHelperOutput.Content.GetContent());
|
||||||
cache.Verify(c => c.Set(
|
cache.Verify(c => c.Set(
|
||||||
/*key*/ It.IsAny<string>(),
|
/*key*/ It.IsAny<string>(),
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ It.IsAny<object>(),
|
||||||
/*state*/ It.IsAny<object>(),
|
/*options*/ It.IsAny<MemoryCacheEntryOptions>()),
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()),
|
|
||||||
Times.Never);
|
Times.Never);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -284,15 +284,16 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
var childContent = "original-child-content";
|
var childContent = "original-child-content";
|
||||||
var cache = new Mock<IMemoryCache>();
|
var cache = new Mock<IMemoryCache>();
|
||||||
cache.CallBase = true;
|
cache.CallBase = true;
|
||||||
|
var value = new DefaultTagHelperContent().SetContent("ok");
|
||||||
|
cache.Setup(c => c.CreateLinkingScope()).Returns(new Mock<IEntryLink>().Object);
|
||||||
cache.Setup(c => c.Set(
|
cache.Setup(c => c.Set(
|
||||||
/*key*/ It.IsAny<string>(),
|
/*key*/ It.IsAny<string>(),
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ It.IsAny<object>(),
|
||||||
/*state*/ It.IsAny<object>(),
|
/*options*/ It.IsAny<MemoryCacheEntryOptions>()))
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
|
.Returns(value)
|
||||||
.Returns(new DefaultTagHelperContent().SetContent("ok"))
|
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
object cacheResult;
|
object cacheResult;
|
||||||
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out cacheResult))
|
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), out cacheResult))
|
||||||
.Returns(false);
|
.Returns(false);
|
||||||
var tagHelperContext = GetTagHelperContext(id, childContent);
|
var tagHelperContext = GetTagHelperContext(id, childContent);
|
||||||
var tagHelperOutput = new TagHelperOutput("cache", new TagHelperAttributeList());
|
var tagHelperOutput = new TagHelperOutput("cache", new TagHelperAttributeList());
|
||||||
|
|
@ -313,9 +314,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
Assert.Equal(childContent, tagHelperOutput.Content.GetContent());
|
Assert.Equal(childContent, tagHelperOutput.Content.GetContent());
|
||||||
cache.Verify(c => c.Set(
|
cache.Verify(c => c.Set(
|
||||||
/*key*/ It.IsAny<string>(),
|
/*key*/ It.IsAny<string>(),
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ It.IsAny<object>(),
|
||||||
/*state*/ It.IsAny<object>(),
|
/*options*/ It.IsAny<MemoryCacheEntryOptions>()),
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()),
|
|
||||||
Times.Once);
|
Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,14 +421,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateCacheContext_SetsAbsoluteExpiration_IfExpiresOnIsSet()
|
public void UpdateCacheEntryOptions_SetsAbsoluteExpiration_IfExpiresOnIsSet()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expiresOn = DateTimeOffset.UtcNow.AddMinutes(4);
|
var expiresOn = DateTimeOffset.UtcNow.AddMinutes(4);
|
||||||
var cache = new MemoryCache(new MemoryCacheOptions());
|
var cache = new MemoryCache(new MemoryCacheOptions());
|
||||||
var cacheContext = new Mock<ICacheSetContext>(MockBehavior.Strict);
|
|
||||||
cacheContext.Setup(c => c.SetAbsoluteExpiration(expiresOn))
|
|
||||||
.Verifiable();
|
|
||||||
var cacheTagHelper = new CacheTagHelper
|
var cacheTagHelper = new CacheTagHelper
|
||||||
{
|
{
|
||||||
MemoryCache = cache,
|
MemoryCache = cache,
|
||||||
|
|
@ -436,21 +433,18 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
cacheTagHelper.UpdateCacheContext(cacheContext.Object, new EntryLink());
|
var cacheEntryOptions = cacheTagHelper.GetMemoryCacheEntryOptions(new EntryLink());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
cacheContext.Verify();
|
Assert.Equal(expiresOn, cacheEntryOptions.AbsoluteExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateCacheContext_UsesAbsoluteExpirationSpecifiedOnEntryLink()
|
public void UpdateCacheEntryOptions_UsesAbsoluteExpirationSpecifiedOnEntryLink()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expiresOn = DateTimeOffset.UtcNow.AddMinutes(7);
|
var expiresOn = DateTimeOffset.UtcNow.AddMinutes(7);
|
||||||
var cache = new MemoryCache(new MemoryCacheOptions());
|
var cache = new MemoryCache(new MemoryCacheOptions());
|
||||||
var cacheContext = new Mock<ICacheSetContext>(MockBehavior.Strict);
|
|
||||||
cacheContext.Setup(c => c.SetAbsoluteExpiration(expiresOn))
|
|
||||||
.Verifiable();
|
|
||||||
var cacheTagHelper = new CacheTagHelper
|
var cacheTagHelper = new CacheTagHelper
|
||||||
{
|
{
|
||||||
MemoryCache = cache
|
MemoryCache = cache
|
||||||
|
|
@ -460,29 +454,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
entryLink.SetAbsoluteExpiration(expiresOn);
|
entryLink.SetAbsoluteExpiration(expiresOn);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
cacheTagHelper.UpdateCacheContext(cacheContext.Object, entryLink);
|
var cacheEntryOptions = cacheTagHelper.GetMemoryCacheEntryOptions(entryLink);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
cacheContext.Verify();
|
Assert.Equal(expiresOn, cacheEntryOptions.AbsoluteExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateCacheContext_PrefersAbsoluteExpirationSpecifiedOnEntryLinkOverExpiresOn()
|
public void UpdateCacheEntryOptions_PrefersAbsoluteExpirationSpecifiedOnEntryLinkOverExpiresOn()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expiresOn1 = DateTimeOffset.UtcNow.AddDays(12);
|
var expiresOn1 = DateTimeOffset.UtcNow.AddDays(12);
|
||||||
var expiresOn2 = DateTimeOffset.UtcNow.AddMinutes(4);
|
var expiresOn2 = DateTimeOffset.UtcNow.AddMinutes(4);
|
||||||
var cache = new MemoryCache(new MemoryCacheOptions());
|
var cache = new MemoryCache(new MemoryCacheOptions());
|
||||||
var cacheContext = new Mock<ICacheSetContext>();
|
|
||||||
var sequence = new MockSequence();
|
|
||||||
cacheContext.InSequence(sequence)
|
|
||||||
.Setup(c => c.SetAbsoluteExpiration(expiresOn1))
|
|
||||||
.Verifiable();
|
|
||||||
|
|
||||||
cacheContext.InSequence(sequence)
|
|
||||||
.Setup(c => c.SetAbsoluteExpiration(expiresOn2))
|
|
||||||
.Verifiable();
|
|
||||||
|
|
||||||
var cacheTagHelper = new CacheTagHelper
|
var cacheTagHelper = new CacheTagHelper
|
||||||
{
|
{
|
||||||
MemoryCache = cache,
|
MemoryCache = cache,
|
||||||
|
|
@ -493,21 +477,18 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
entryLink.SetAbsoluteExpiration(expiresOn2);
|
entryLink.SetAbsoluteExpiration(expiresOn2);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
cacheTagHelper.UpdateCacheContext(cacheContext.Object, entryLink);
|
var cacheEntryOptions = cacheTagHelper.GetMemoryCacheEntryOptions(entryLink);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
cacheContext.Verify();
|
Assert.Equal(expiresOn2, cacheEntryOptions.AbsoluteExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateCacheContext_SetsAbsoluteExpiration_IfExpiresAfterIsSet()
|
public void UpdateCacheEntryOptions_SetsAbsoluteExpiration_IfExpiresAfterIsSet()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expiresAfter = TimeSpan.FromSeconds(42);
|
var expiresAfter = TimeSpan.FromSeconds(42);
|
||||||
var cache = new MemoryCache(new MemoryCacheOptions());
|
var cache = new MemoryCache(new MemoryCacheOptions());
|
||||||
var cacheContext = new Mock<ICacheSetContext>();
|
|
||||||
cacheContext.Setup(c => c.SetAbsoluteExpiration(expiresAfter))
|
|
||||||
.Verifiable();
|
|
||||||
var cacheTagHelper = new CacheTagHelper
|
var cacheTagHelper = new CacheTagHelper
|
||||||
{
|
{
|
||||||
MemoryCache = cache,
|
MemoryCache = cache,
|
||||||
|
|
@ -515,21 +496,18 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
cacheTagHelper.UpdateCacheContext(cacheContext.Object, new EntryLink());
|
var cacheEntryOptions = cacheTagHelper.GetMemoryCacheEntryOptions(new EntryLink());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
cacheContext.Verify();
|
Assert.Equal(expiresAfter, cacheEntryOptions.AbsoluteExpirationRelativeToNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateCacheContext_SetsSlidingExpiration_IfExpiresSlidingIsSet()
|
public void UpdateCacheEntryOptions_SetsSlidingExpiration_IfExpiresSlidingIsSet()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expiresSliding = TimeSpan.FromSeconds(37);
|
var expiresSliding = TimeSpan.FromSeconds(37);
|
||||||
var cache = new MemoryCache(new MemoryCacheOptions());
|
var cache = new MemoryCache(new MemoryCacheOptions());
|
||||||
var cacheContext = new Mock<ICacheSetContext>();
|
|
||||||
cacheContext.Setup(c => c.SetSlidingExpiration(expiresSliding))
|
|
||||||
.Verifiable();
|
|
||||||
var cacheTagHelper = new CacheTagHelper
|
var cacheTagHelper = new CacheTagHelper
|
||||||
{
|
{
|
||||||
MemoryCache = cache,
|
MemoryCache = cache,
|
||||||
|
|
@ -537,21 +515,18 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
cacheTagHelper.UpdateCacheContext(cacheContext.Object, new EntryLink());
|
var cacheEntryOptions = cacheTagHelper.GetMemoryCacheEntryOptions(new EntryLink());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
cacheContext.Verify();
|
Assert.Equal(expiresSliding, cacheEntryOptions.SlidingExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateCacheContext_SetsCachePreservationPriority()
|
public void UpdateCacheEntryOptions_SetsCachePreservationPriority()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var priority = CachePreservationPriority.High;
|
var priority = CacheItemPriority.High;
|
||||||
var cache = new MemoryCache(new MemoryCacheOptions());
|
var cache = new MemoryCache(new MemoryCacheOptions());
|
||||||
var cacheContext = new Mock<ICacheSetContext>();
|
|
||||||
cacheContext.Setup(c => c.SetPriority(priority))
|
|
||||||
.Verifiable();
|
|
||||||
var cacheTagHelper = new CacheTagHelper
|
var cacheTagHelper = new CacheTagHelper
|
||||||
{
|
{
|
||||||
MemoryCache = cache,
|
MemoryCache = cache,
|
||||||
|
|
@ -559,26 +534,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
cacheTagHelper.UpdateCacheContext(cacheContext.Object, new EntryLink());
|
var cacheEntryOptions = cacheTagHelper.GetMemoryCacheEntryOptions(new EntryLink());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
cacheContext.Verify();
|
Assert.Equal(priority, cacheEntryOptions.Priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateCacheContext_CopiesTriggersFromEntryLink()
|
public void UpdateCacheEntryOptions_CopiesTriggersFromEntryLink()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expiresSliding = TimeSpan.FromSeconds(30);
|
var expiresSliding = TimeSpan.FromSeconds(30);
|
||||||
var expected = new[] { Mock.Of<IExpirationTrigger>(), Mock.Of<IExpirationTrigger>() };
|
var expected = new[] { Mock.Of<IExpirationTrigger>(), Mock.Of<IExpirationTrigger>() };
|
||||||
var triggers = new List<IExpirationTrigger>();
|
|
||||||
var cache = new MemoryCache(new MemoryCacheOptions());
|
var cache = new MemoryCache(new MemoryCacheOptions());
|
||||||
var cacheContext = new Mock<ICacheSetContext>();
|
|
||||||
cacheContext.Setup(c => c.SetSlidingExpiration(expiresSliding))
|
|
||||||
.Verifiable();
|
|
||||||
cacheContext.Setup(c => c.AddExpirationTrigger(It.IsAny<IExpirationTrigger>()))
|
|
||||||
.Callback<IExpirationTrigger>(triggers.Add)
|
|
||||||
.Verifiable();
|
|
||||||
var cacheTagHelper = new CacheTagHelper
|
var cacheTagHelper = new CacheTagHelper
|
||||||
{
|
{
|
||||||
MemoryCache = cache,
|
MemoryCache = cache,
|
||||||
|
|
@ -589,11 +557,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
entryLink.AddExpirationTriggers(expected);
|
entryLink.AddExpirationTriggers(expected);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
cacheTagHelper.UpdateCacheContext(cacheContext.Object, entryLink);
|
var cacheEntryOptions = cacheTagHelper.GetMemoryCacheEntryOptions(entryLink);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
cacheContext.Verify();
|
Assert.Equal(expected, cacheEntryOptions.Triggers.ToArray());
|
||||||
Assert.Equal(expected, triggers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -770,19 +737,22 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
expectedContent.SetContent("some-content");
|
expectedContent.SetContent("some-content");
|
||||||
var tokenSource = new CancellationTokenSource();
|
var tokenSource = new CancellationTokenSource();
|
||||||
var cache = new MemoryCache(new MemoryCacheOptions());
|
var cache = new MemoryCache(new MemoryCacheOptions());
|
||||||
|
var cacheEntryOptions = new MemoryCacheEntryOptions()
|
||||||
|
.AddExpirationTrigger(new CancellationTokenTrigger(tokenSource.Token));
|
||||||
var tagHelperContext = new TagHelperContext(
|
var tagHelperContext = new TagHelperContext(
|
||||||
allAttributes: new TagHelperAttributeList(),
|
allAttributes: new TagHelperAttributeList(),
|
||||||
items: new Dictionary<object, object>(),
|
items: new Dictionary<object, object>(),
|
||||||
uniqueId: id,
|
uniqueId: id,
|
||||||
getChildContentAsync: () =>
|
getChildContentAsync: () =>
|
||||||
{
|
{
|
||||||
var entryLink = EntryLinkHelpers.ContextLink;
|
TagHelperContent tagHelperContent;
|
||||||
Assert.NotNull(entryLink);
|
if(!cache.TryGetValue("key1", out tagHelperContent))
|
||||||
entryLink.AddExpirationTriggers(new[]
|
|
||||||
{
|
{
|
||||||
new CancellationTokenTrigger(tokenSource.Token)
|
tagHelperContent = expectedContent;
|
||||||
});
|
cache.Set("key1", tagHelperContent, cacheEntryOptions);
|
||||||
return Task.FromResult<TagHelperContent>(expectedContent);
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(tagHelperContent);
|
||||||
});
|
});
|
||||||
var tagHelperOutput = new TagHelperOutput("cache", new TagHelperAttributeList { { "attr", "value" } });
|
var tagHelperOutput = new TagHelperOutput("cache", new TagHelperAttributeList { { "attr", "value" } });
|
||||||
tagHelperOutput.PreContent.SetContent("<cache>");
|
tagHelperOutput.PreContent.SetContent("<cache>");
|
||||||
|
|
|
||||||
|
|
@ -260,29 +260,14 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
object result = null;
|
object result = null;
|
||||||
var cache = new Mock<IMemoryCache>();
|
var cache = new Mock<IMemoryCache>();
|
||||||
cache.CallBase = true;
|
cache.CallBase = true;
|
||||||
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out result))
|
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), out result))
|
||||||
.Returns(result != null);
|
.Returns(result != null);
|
||||||
|
cache.Setup(
|
||||||
var cacheSetContext = new Mock<ICacheSetContext>();
|
|
||||||
cacheSetContext.Setup(c => c.AddExpirationTrigger(It.IsAny<IExpirationTrigger>()));
|
|
||||||
cache
|
|
||||||
.Setup(
|
|
||||||
c => c.Set(
|
c => c.Set(
|
||||||
/*key*/ It.IsAny<string>(),
|
/*key*/ It.IsAny<string>(),
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ It.IsAny<object>(),
|
||||||
/*state*/ It.IsAny<object>(),
|
/*options*/ It.IsAny<MemoryCacheEntryOptions>()))
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
|
.Returns(new object());
|
||||||
.Returns((
|
|
||||||
string input,
|
|
||||||
IEntryLink entryLink,
|
|
||||||
object state,
|
|
||||||
Func<ICacheSetContext, object> create) =>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
cacheSetContext.Setup(c => c.State).Returns(state);
|
|
||||||
return create(cacheSetContext.Object);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return cache.Object;
|
return cache.Object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,21 +141,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
object cacheValue = null;
|
object cacheValue = null;
|
||||||
var cache = new Mock<IMemoryCache>();
|
var cache = new Mock<IMemoryCache>();
|
||||||
cache.CallBase = true;
|
cache.CallBase = true;
|
||||||
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out cacheValue))
|
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), out cacheValue))
|
||||||
.Returns(cacheValue != null);
|
.Returns(cacheValue != null);
|
||||||
var cacheSetContext = new Mock<ICacheSetContext>();
|
|
||||||
cacheSetContext.Setup(c => c.AddExpirationTrigger(trigger.Object)).Verifiable();
|
|
||||||
cache.Setup(c => c.Set(
|
cache.Setup(c => c.Set(
|
||||||
/*key*/ filePath,
|
/*key*/ filePath,
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ It.IsAny<object>(),
|
||||||
/*state*/ It.IsAny<object>(),
|
/*options*/ It.IsAny<MemoryCacheEntryOptions>()))
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
|
.Returns(new object())
|
||||||
.Returns<string, IEntryLink, object, Func<ICacheSetContext, object>>(
|
|
||||||
(key, link, state, create) =>
|
|
||||||
{
|
|
||||||
cacheSetContext.Setup(c => c.State).Returns(state);
|
|
||||||
return create(cacheSetContext.Object);
|
|
||||||
})
|
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var fileVersionProvider = new FileVersionProvider(
|
var fileVersionProvider = new FileVersionProvider(
|
||||||
hostingEnvironment.WebRootFileProvider,
|
hostingEnvironment.WebRootFileProvider,
|
||||||
|
|
@ -167,7 +159,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(filePath + "?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", result);
|
Assert.Equal(filePath + "?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", result);
|
||||||
cacheSetContext.VerifyAll();
|
|
||||||
cache.VerifyAll();
|
cache.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,29 +202,16 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
{
|
{
|
||||||
var cache = new Mock<IMemoryCache>();
|
var cache = new Mock<IMemoryCache>();
|
||||||
cache.CallBase = true;
|
cache.CallBase = true;
|
||||||
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out result))
|
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), out result))
|
||||||
.Returns(result != null);
|
.Returns(result != null);
|
||||||
|
|
||||||
var cacheSetContext = new Mock<ICacheSetContext>();
|
|
||||||
cacheSetContext.Setup(c => c.AddExpirationTrigger(It.IsAny<IExpirationTrigger>()));
|
|
||||||
cache
|
cache
|
||||||
.Setup(
|
.Setup(
|
||||||
c => c.Set(
|
c => c.Set(
|
||||||
/*key*/ It.IsAny<string>(),
|
/*key*/ It.IsAny<string>(),
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ It.IsAny<object>(),
|
||||||
/*state*/ It.IsAny<object>(),
|
/*options*/ It.IsAny<MemoryCacheEntryOptions>()))
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
|
.Returns(new object());
|
||||||
.Returns((
|
|
||||||
string input,
|
|
||||||
IEntryLink entryLink,
|
|
||||||
object state,
|
|
||||||
Func<ICacheSetContext, object> create) =>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
cacheSetContext.Setup(c => c.State).Returns(state);
|
|
||||||
return create(cacheSetContext.Object);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return cache.Object;
|
return cache.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -252,18 +252,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var fileProvider = MakeFileProvider();
|
var fileProvider = MakeFileProvider();
|
||||||
var cache = MakeCache(new List<string> { "/blank.css", "/site.css" });
|
var expected = new List<string> { "/blank.css", "/site.css" };
|
||||||
|
var cache = MakeCache(result: expected);
|
||||||
var requestPathBase = PathString.Empty;
|
var requestPathBase = PathString.Empty;
|
||||||
var globbingUrlBuilder = new GlobbingUrlBuilder(fileProvider, cache, requestPathBase);
|
var globbingUrlBuilder = new GlobbingUrlBuilder(fileProvider, cache, requestPathBase);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var urlList = globbingUrlBuilder.BuildUrlList(
|
var actual = globbingUrlBuilder.BuildUrlList(
|
||||||
staticUrl: null,
|
staticUrl: null,
|
||||||
includePattern: "**/*.css",
|
includePattern: "**/*.css",
|
||||||
excludePattern: null);
|
excludePattern: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(urlList,
|
Assert.Collection(actual,
|
||||||
url => Assert.Equal("/blank.css", url),
|
url => Assert.Equal("/blank.css", url),
|
||||||
url => Assert.Equal("/site.css", url));
|
url => Assert.Equal("/site.css", url));
|
||||||
}
|
}
|
||||||
|
|
@ -276,19 +277,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
var fileProvider = MakeFileProvider(MakeDirectoryContents("site.css", "blank.css"));
|
var fileProvider = MakeFileProvider(MakeDirectoryContents("site.css", "blank.css"));
|
||||||
Mock.Get(fileProvider).Setup(f => f.Watch(It.IsAny<string>())).Returns(trigger.Object);
|
Mock.Get(fileProvider).Setup(f => f.Watch(It.IsAny<string>())).Returns(trigger.Object);
|
||||||
var cache = MakeCache();
|
var cache = MakeCache();
|
||||||
var cacheSetContext = new Mock<ICacheSetContext>();
|
|
||||||
cacheSetContext.Setup(c => c.AddExpirationTrigger(trigger.Object)).Verifiable();
|
|
||||||
Mock.Get(cache).Setup(c => c.Set(
|
Mock.Get(cache).Setup(c => c.Set(
|
||||||
/*key*/ It.IsAny<string>(),
|
/*key*/ It.IsAny<string>(),
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ It.IsAny<object>(),
|
||||||
/*state*/ It.IsAny<object>(),
|
/*options*/ It.IsAny<MemoryCacheEntryOptions>()))
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
|
.Returns(new object())
|
||||||
.Returns<string, IEntryLink, object, Func<ICacheSetContext, object>>(
|
|
||||||
(key, link, state, create) =>
|
|
||||||
{
|
|
||||||
cacheSetContext.Setup(c => c.State).Returns(state);
|
|
||||||
return create(cacheSetContext.Object);
|
|
||||||
})
|
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var requestPathBase = PathString.Empty;
|
var requestPathBase = PathString.Empty;
|
||||||
var globbingUrlBuilder = new GlobbingUrlBuilder(fileProvider, cache, requestPathBase);
|
var globbingUrlBuilder = new GlobbingUrlBuilder(fileProvider, cache, requestPathBase);
|
||||||
|
|
@ -303,7 +296,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
Assert.Collection(urlList,
|
Assert.Collection(urlList,
|
||||||
url => Assert.Equal("/blank.css", url),
|
url => Assert.Equal("/blank.css", url),
|
||||||
url => Assert.Equal("/site.css", url));
|
url => Assert.Equal("/site.css", url));
|
||||||
cacheSetContext.VerifyAll();
|
|
||||||
Mock.Get(cache).VerifyAll();
|
Mock.Get(cache).VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -394,11 +386,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
{
|
{
|
||||||
throw new ArgumentException(nameof(rootNode));
|
throw new ArgumentException(nameof(rootNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileProvider = new Mock<IFileProvider>(MockBehavior.Strict);
|
var fileProvider = new Mock<IFileProvider>(MockBehavior.Strict);
|
||||||
fileProvider.Setup(fp => fp.GetDirectoryContents(string.Empty))
|
fileProvider.Setup(fp => fp.GetDirectoryContents(string.Empty))
|
||||||
.Returns(MakeDirectoryContents(rootNode, fileProvider));
|
.Returns(MakeDirectoryContents(rootNode, fileProvider));
|
||||||
|
|
||||||
return fileProvider.Object;
|
return fileProvider.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -418,7 +410,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
.Returns(MakeDirectoryContents(node, fileProviderMock));
|
.Returns(MakeDirectoryContents(node, fileProviderMock));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var directoryContents = new Mock<IDirectoryContents>();
|
var directoryContents = new Mock<IDirectoryContents>();
|
||||||
directoryContents.Setup(dc => dc.GetEnumerator()).Returns(children.GetEnumerator());
|
directoryContents.Setup(dc => dc.GetEnumerator()).Returns(children.GetEnumerator());
|
||||||
|
|
||||||
|
|
@ -450,7 +442,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
||||||
private static IMemoryCache MakeCache(object result = null)
|
private static IMemoryCache MakeCache(object result = null)
|
||||||
{
|
{
|
||||||
var cache = new Mock<IMemoryCache>();
|
var cache = new Mock<IMemoryCache>();
|
||||||
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out result))
|
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), out result))
|
||||||
.Returns(result != null);
|
.Returns(result != null);
|
||||||
return cache.Object;
|
return cache.Object;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -760,29 +760,16 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
{
|
{
|
||||||
var cache = new Mock<IMemoryCache>();
|
var cache = new Mock<IMemoryCache>();
|
||||||
cache.CallBase = true;
|
cache.CallBase = true;
|
||||||
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out result))
|
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), out result))
|
||||||
.Returns(result != null);
|
.Returns(result != null);
|
||||||
|
|
||||||
var cacheSetContext = new Mock<ICacheSetContext>();
|
|
||||||
cacheSetContext.Setup(c => c.AddExpirationTrigger(It.IsAny<IExpirationTrigger>()));
|
|
||||||
cache
|
cache
|
||||||
.Setup(
|
.Setup(
|
||||||
c => c.Set(
|
c => c.Set(
|
||||||
/*key*/ It.IsAny<string>(),
|
/*key*/ It.IsAny<string>(),
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ It.IsAny<object>(),
|
||||||
/*state*/ It.IsAny<object>(),
|
/*options*/ It.IsAny<MemoryCacheEntryOptions>()))
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
|
.Returns(result);
|
||||||
.Returns((
|
|
||||||
string input,
|
|
||||||
IEntryLink entryLink,
|
|
||||||
object state,
|
|
||||||
Func<ICacheSetContext, object> create) =>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
cacheSetContext.Setup(c => c.State).Returns(state);
|
|
||||||
return create(cacheSetContext.Object);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return cache.Object;
|
return cache.Object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -842,29 +842,18 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
{
|
{
|
||||||
var cache = new Mock<IMemoryCache>();
|
var cache = new Mock<IMemoryCache>();
|
||||||
cache.CallBase = true;
|
cache.CallBase = true;
|
||||||
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out result))
|
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), out result))
|
||||||
.Returns(result != null);
|
.Returns(result != null);
|
||||||
|
|
||||||
var cacheSetContext = new Mock<ICacheSetContext>();
|
var cacheEntryOptions = new MemoryCacheEntryOptions();
|
||||||
cacheSetContext.Setup(c => c.AddExpirationTrigger(It.IsAny<IExpirationTrigger>()));
|
cacheEntryOptions.AddExpirationTrigger(new Mock<IExpirationTrigger>().Object);
|
||||||
cache
|
cache
|
||||||
.Setup(
|
.Setup(
|
||||||
c => c.Set(
|
c => c.Set(
|
||||||
/*key*/ It.IsAny<string>(),
|
/*key*/ It.IsAny<string>(),
|
||||||
/*link*/ It.IsAny<IEntryLink>(),
|
/*value*/ It.IsAny<object>(),
|
||||||
/*state*/ It.IsAny<object>(),
|
/*options*/ cacheEntryOptions))
|
||||||
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
|
.Returns(result);
|
||||||
.Returns((
|
|
||||||
string input,
|
|
||||||
IEntryLink entryLink,
|
|
||||||
object state,
|
|
||||||
Func<ICacheSetContext, object> create) =>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
cacheSetContext.Setup(c => c.State).Returns(state);
|
|
||||||
return create(cacheSetContext.Object);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return cache.Object;
|
return cache.Object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,18 @@ namespace MvcTagHelpersWebSite.Components
|
||||||
[Activate]
|
[Activate]
|
||||||
public ProductsService ProductsService { get; set; }
|
public ProductsService ProductsService { get; set; }
|
||||||
|
|
||||||
|
[Activate]
|
||||||
|
public IMemoryCache Cache { get; set; }
|
||||||
|
|
||||||
public IViewComponentResult Invoke(string category)
|
public IViewComponentResult Invoke(string category)
|
||||||
{
|
{
|
||||||
IExpirationTrigger trigger;
|
string products;
|
||||||
var products = ProductsService.GetProducts(category, out trigger);
|
if (!Cache.TryGetValue(category, out products))
|
||||||
EntryLinkHelpers.ContextLink.AddExpirationTriggers(new[] { trigger });
|
{
|
||||||
|
IExpirationTrigger trigger;
|
||||||
|
products = ProductsService.GetProducts(category, out trigger);
|
||||||
|
Cache.Set(category, products, new MemoryCacheEntryOptions().AddExpirationTrigger(trigger));
|
||||||
|
}
|
||||||
|
|
||||||
ViewData["Products"] = products;
|
ViewData["Products"] = products;
|
||||||
return View();
|
return View();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@using Microsoft.Framework.Caching.Memory
|
@using Microsoft.Framework.Caching.Memory
|
||||||
<cache expires-after="TimeSpan.FromSeconds(1)" priority="CachePreservationPriority.Low">
|
<cache expires-after="TimeSpan.FromSeconds(1)" priority="CacheItemPriority.Low">
|
||||||
Cached content for @ViewBag.ProductId
|
Cached content for @ViewBag.ProductId
|
||||||
</cache>
|
</cache>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue