#2142: Introducing enabled attribute for CacheTagHelper.

This commit is contained in:
sornaks 2015-04-06 16:37:21 -07:00
parent 43e24b2aad
commit b08a2154c1
5 changed files with 150 additions and 18 deletions

View File

@ -33,6 +33,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
private const string ExpiresSlidingAttributeName = "expires-sliding";
private const string CachePriorityAttributeName = "priority";
private const string CacheKeyTokenSeparator = "||";
private const string EnabledAttributeName = "enabled";
private static readonly char[] AttributeSeparator = new[] { ',' };
/// <summary>
@ -108,31 +109,48 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[HtmlAttributeName(CachePriorityAttributeName)]
public CachePreservationPriority? Priority { get; set; }
/// <summary>
/// Gets or sets the value which determines if the tag helper is enabled or not.
/// </summary>
[HtmlAttributeName(EnabledAttributeName)]
public bool Enabled { get; set; } = true;
/// <inheritdoc />
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var key = GenerateKey(context);
TagHelperContent result;
if (!MemoryCache.TryGetValue(key, out result))
TagHelperContent result = null;
if (Enabled)
{
// Create an EntryLink and flow it so that it is accessible via the ambient EntryLinkHelpers.ContentLink
// for user code.
var entryLink = new EntryLink();
using (entryLink.FlowContext())
var key = GenerateKey(context);
if (!MemoryCache.TryGetValue(key, out result))
{
result = await context.GetChildContentAsync();
}
// Create an EntryLink and flow it so that it is accessible via the ambient
// EntryLinkHelpers.ContextLink for user code.
var entryLink = new EntryLink();
using (entryLink.FlowContext())
{
result = await context.GetChildContentAsync();
}
MemoryCache.Set(key, cacheSetContext =>
{
UpdateCacheContext(cacheSetContext, entryLink);
return result;
});
MemoryCache.Set(key, cacheSetContext =>
{
UpdateCacheContext(cacheSetContext, entryLink);
return result;
});
}
}
// Clear the contents of the "cache" element since we don't want to render it.
output.SuppressOutput();
output.Content.SetContent(result);
if (Enabled)
{
output.Content.SetContent(result);
}
else
{
result = await context.GetChildContentAsync();
output.Content.SetContent(result);
}
}
// Internal for unit testing

View File

@ -228,6 +228,30 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(expected2, response2.Trim());
}
[Fact]
public async Task CacheTagHelper_Activated_BasedOnEnabledParameter()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
client.BaseAddress = new Uri("http://localhost");
// Act
// This is cached for 1s.
var response1 = await client.GetStringAsync("/catalog/31");
var response2 = await client.GetStringAsync("/catalog/32");
// This request is made before the cache expires with enabled set to false.
client.DefaultRequestHeaders.Remove("IsCacheEnabled");
client.DefaultRequestHeaders.Add("IsCacheEnabled", "false");
var response3 = await client.GetStringAsync("/catalog/156");
// Assert
Assert.Equal("Cached content for 31", response1.Trim());
Assert.Equal("Cached content for 31", response2.Trim());
Assert.Equal("Cached content for 156", response3.Trim());
}
[Fact]
public async Task CacheTagHelper_UsesVaryByCookie_ToVaryContent()
{

View File

@ -236,6 +236,89 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.Equal(expected, key);
}
[Fact]
public async Task ProcessAsync_DoesNotCache_IfDisabled()
{
// Arrange
var id = "unique-id";
var childContent = "original-child-content";
var cache = new Mock<IMemoryCache>();
cache.CallBase = true;
cache.Setup(c => c.Set(
/*key*/ It.IsAny<string>(),
/*link*/ It.IsAny<IEntryLink>(),
/*state*/ It.IsAny<object>(),
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
.Returns(new DefaultTagHelperContent().SetContent("ok"))
.Verifiable();
object cacheResult;
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out cacheResult))
.Returns(false);
var tagHelperContext = GetTagHelperContext(id, childContent);
var tagHelperOutput = new TagHelperOutput("cache", new Dictionary<string, object>());
var cacheTagHelper = new CacheTagHelper
{
ViewContext = GetViewContext(),
MemoryCache = cache.Object,
Enabled = false
};
// Act
await cacheTagHelper.ProcessAsync(tagHelperContext, tagHelperOutput);
// Assert
Assert.Equal(childContent, tagHelperOutput.Content.GetContent());
cache.Verify(c => c.Set(
/*key*/ It.IsAny<string>(),
/*link*/ It.IsAny<IEntryLink>(),
/*state*/ It.IsAny<object>(),
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()),
Times.Never);
}
[Fact]
public async Task ProcessAsync_ReturnsCachedValue_IfEnabled()
{
// Arrange
var id = "unique-id";
var childContent = "original-child-content";
var cache = new Mock<IMemoryCache>();
cache.CallBase = true;
cache.Setup(c => c.Set(
/*key*/ It.IsAny<string>(),
/*link*/ It.IsAny<IEntryLink>(),
/*state*/ It.IsAny<object>(),
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()))
.Returns(new DefaultTagHelperContent().SetContent("ok"))
.Verifiable();
object cacheResult;
cache.Setup(c => c.TryGetValue(It.IsAny<string>(), It.IsAny<IEntryLink>(), out cacheResult))
.Returns(false);
var tagHelperContext = GetTagHelperContext(id, childContent);
var tagHelperOutput = new TagHelperOutput("cache", new Dictionary<string, object>());
var cacheTagHelper = new CacheTagHelper
{
ViewContext = GetViewContext(),
MemoryCache = cache.Object,
Enabled = true
};
// Act
await cacheTagHelper.ProcessAsync(tagHelperContext, tagHelperOutput);
// Assert
Assert.Empty(tagHelperOutput.PreContent.GetContent());
Assert.Empty(tagHelperOutput.PostContent.GetContent());
Assert.True(tagHelperOutput.IsContentModified);
Assert.Equal(childContent, tagHelperOutput.Content.GetContent());
cache.Verify(c => c.Set(
/*key*/ It.IsAny<string>(),
/*link*/ It.IsAny<IEntryLink>(),
/*state*/ It.IsAny<object>(),
/*create*/ It.IsAny<Func<ICacheSetContext, object>>()),
Times.Once);
}
[Fact]
public async Task ProcessAsync_ReturnsCachedValue_IfVaryByParamIsUnchanged()
{

View File

@ -12,7 +12,7 @@ namespace MvcTagHelpersWebSite.Controllers
public ProductsService ProductsService { get; set; }
[HttpGet("/catalog")]
public ViewResult Splash(int categoryId, int correlationId, [FromHeader]string locale)
public ViewResult Splash(int categoryId, int correlationId, [FromHeader] string locale)
{
var category = categoryId == 1 ? "Laptops" : "Phones";
ViewData["Category"] = category;
@ -23,8 +23,15 @@ namespace MvcTagHelpersWebSite.Controllers
}
[HttpGet("/catalog/{id:int}")]
public ViewResult Details(int id)
public ViewResult Details(int id, [FromHeader] string isCacheEnabled)
{
bool cacheEnabledHeader = true;
if (!string.IsNullOrEmpty(isCacheEnabled))
{
bool.TryParse(isCacheEnabled, out cacheEnabledHeader);
}
ViewBag.IsCacheEnabled = cacheEnabledHeader;
ViewData["ProductId"] = id;
return View();
}

View File

@ -1,4 +1,4 @@
@using Microsoft.Framework.Caching.Memory
<cache expires-after="TimeSpan.FromSeconds(1)" priority="CachePreservationPriority.Low">
<cache expires-after="TimeSpan.FromSeconds(1)" priority="CachePreservationPriority.Low" enabled="ViewBag.IsCacheEnabled">
Cached content for @ViewBag.ProductId
</cache>