#2142: Introducing enabled attribute for CacheTagHelper.
This commit is contained in:
parent
43e24b2aad
commit
b08a2154c1
|
|
@ -33,6 +33,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
private const string ExpiresSlidingAttributeName = "expires-sliding";
|
private const string ExpiresSlidingAttributeName = "expires-sliding";
|
||||||
private const string CachePriorityAttributeName = "priority";
|
private const string CachePriorityAttributeName = "priority";
|
||||||
private const string CacheKeyTokenSeparator = "||";
|
private const string CacheKeyTokenSeparator = "||";
|
||||||
|
private const string EnabledAttributeName = "enabled";
|
||||||
private static readonly char[] AttributeSeparator = new[] { ',' };
|
private static readonly char[] AttributeSeparator = new[] { ',' };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -108,31 +109,48 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
[HtmlAttributeName(CachePriorityAttributeName)]
|
[HtmlAttributeName(CachePriorityAttributeName)]
|
||||||
public CachePreservationPriority? Priority { get; set; }
|
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 />
|
/// <inheritdoc />
|
||||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
var key = GenerateKey(context);
|
TagHelperContent result = null;
|
||||||
TagHelperContent result;
|
if (Enabled)
|
||||||
if (!MemoryCache.TryGetValue(key, out result))
|
|
||||||
{
|
{
|
||||||
// Create an EntryLink and flow it so that it is accessible via the ambient EntryLinkHelpers.ContentLink
|
var key = GenerateKey(context);
|
||||||
// for user code.
|
if (!MemoryCache.TryGetValue(key, out result))
|
||||||
var entryLink = new EntryLink();
|
|
||||||
using (entryLink.FlowContext())
|
|
||||||
{
|
{
|
||||||
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 =>
|
MemoryCache.Set(key, cacheSetContext =>
|
||||||
{
|
{
|
||||||
UpdateCacheContext(cacheSetContext, entryLink);
|
UpdateCacheContext(cacheSetContext, entryLink);
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the contents of the "cache" element since we don't want to render it.
|
// Clear the contents of the "cache" element since we don't want to render it.
|
||||||
output.SuppressOutput();
|
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
|
// Internal for unit testing
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,30 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal(expected2, response2.Trim());
|
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]
|
[Fact]
|
||||||
public async Task CacheTagHelper_UsesVaryByCookie_ToVaryContent()
|
public async Task CacheTagHelper_UsesVaryByCookie_ToVaryContent()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,89 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
Assert.Equal(expected, key);
|
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]
|
[Fact]
|
||||||
public async Task ProcessAsync_ReturnsCachedValue_IfVaryByParamIsUnchanged()
|
public async Task ProcessAsync_ReturnsCachedValue_IfVaryByParamIsUnchanged()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace MvcTagHelpersWebSite.Controllers
|
||||||
public ProductsService ProductsService { get; set; }
|
public ProductsService ProductsService { get; set; }
|
||||||
|
|
||||||
[HttpGet("/catalog")]
|
[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";
|
var category = categoryId == 1 ? "Laptops" : "Phones";
|
||||||
ViewData["Category"] = category;
|
ViewData["Category"] = category;
|
||||||
|
|
@ -23,8 +23,15 @@ namespace MvcTagHelpersWebSite.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("/catalog/{id:int}")]
|
[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;
|
ViewData["ProductId"] = id;
|
||||||
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="CachePreservationPriority.Low" enabled="ViewBag.IsCacheEnabled">
|
||||||
Cached content for @ViewBag.ProductId
|
Cached content for @ViewBag.ProductId
|
||||||
</cache>
|
</cache>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue