/// <inheritdoc />
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (output == null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            IHtmlContent content = null;

            // Create a cancellation token that will be used
            // to release the task from the memory cache.
            var tokenSource = new CancellationTokenSource();

            if (Enabled)
            {
                var cacheKey = new CacheTagKey(this);

                content = await _distributedCacheService.ProcessContentAsync(output, cacheKey, GetDistributedCacheEntryOptions());
            }
            else
            {
                content = await output.GetChildContentAsync();
            }

            // Clear the contents of the "cache" element since we don't want to render it.
            output.SuppressOutput();

            output.Content.SetHtmlContent(content);
        }
Beispiel #2
0
        public void Equals_ReturnsFalseOnDifferentKey()
        {
            // Arrange
            var tagHelperContext1 = GetTagHelperContext("some-id");
            var cacheTagHelper1 = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext()
            };

            var tagHelperContext2 = GetTagHelperContext("some-other-id");
            var cacheTagHelper2 = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext()
            };

            // Act
            var cacheTagKey1 = new CacheTagKey(cacheTagHelper1, tagHelperContext1);
            var cacheTagKey2 = new CacheTagKey(cacheTagHelper2, tagHelperContext2);

            // Assert
            Assert.NotEqual(cacheTagKey1, cacheTagKey2);
        }
Beispiel #3
0
        public void Equals_ReturnsTrueOnSameKey()
        {
            // Arrange
            var id = Guid.NewGuid().ToString();
            var tagHelperContext1 = GetTagHelperContext(id);
            var cacheTagHelper1 = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext()
            };

            var tagHelperContext2 = GetTagHelperContext(id);
            var cacheTagHelper2 = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext()
            };

            // Act
            var cacheTagKey1 = new CacheTagKey(cacheTagHelper1, tagHelperContext1);
            var cacheTagKey2 = new CacheTagKey(cacheTagHelper2, tagHelperContext2);

            // Assert
            Assert.Equal(cacheTagKey1, cacheTagKey2);
        }
Beispiel #4
0
        public void GenerateKey_ReturnsKeyBasedOnTagHelperName()
        {
            // Arrange
            var name = "some-name";
            var tagHelperContext = GetTagHelperContext();
            var cacheTagHelper = new DistributedCacheTagHelper(
                Mock.Of<IDistributedCacheTagHelperService>(),
                new HtmlTestEncoder())
            {
                ViewContext = GetViewContext(),
                Name = name
            };
            var expected = "DistributedCacheTagHelper||" + name;

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #5
0
        public void GetHashCode_VariesByUniqueId()
        {
            // Arrange
            var tagHelperContext1 = GetTagHelperContext("some-id");
            var cacheTagHelper1 = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext()
            };

            var tagHelperContext2 = GetTagHelperContext("some-other-id");
            var cacheTagHelper2 = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext()
            };

            var cacheKey1 = new CacheTagKey(cacheTagHelper1, tagHelperContext1);
            var cacheKey2 = new CacheTagKey(cacheTagHelper2, tagHelperContext2);

            // Act
            var hashcode1 = cacheKey1.GetHashCode();
            var hashcode2 = cacheKey2.GetHashCode();

            // Assert
            Assert.NotEqual(hashcode1, hashcode2);
        }
Beispiel #6
0
        public void GenerateKey_WithMultipleVaryByOptions_CreatesCombinedKey()
        {
            // Arrange
            var expected = "CacheTagHelper||testid||VaryBy||custom-value||" +
                "VaryByHeader(content-type||text/html)||VaryByUser||someuser";
            var tagHelperContext = GetTagHelperContext();
            var cacheTagHelper = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext(),
                VaryByUser = true,
                VaryByHeader = "content-type",
                VaryBy = "custom-value"
            };
            cacheTagHelper.ViewContext.HttpContext.Request.Headers["Content-Type"] = "text/html";
            var identity = new ClaimsIdentity(new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, "someuser") });
            cacheTagHelper.ViewContext.HttpContext.User = new ClaimsPrincipal(identity);

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #7
0
        public void GenerateKey_UsesVaryByUser_WhenUserIsNotAuthenticated()
        {
            // Arrange
            var expected = "CacheTagHelper||testid||VaryByUser||";
            var tagHelperContext = GetTagHelperContext();
            var cacheTagHelper = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext(),
                VaryByUser = true
            };

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #8
0
        public void GenerateKey_UsesVaryByUserAndAuthenticatedUserName()
        {
            // Arrange
            var expected = "CacheTagHelper||testid||VaryByUser||test_name";
            var tagHelperContext = GetTagHelperContext();
            var cacheTagHelper = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext(),
                VaryByUser = true
            };
            var identity = new ClaimsIdentity(new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, "test_name") });
            cacheTagHelper.ViewContext.HttpContext.User = new ClaimsPrincipal(identity);

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #9
0
        public void GenerateKey_UsesVaryByRoute(string varyByRoute, string expected)
        {
            // Arrange
            var tagHelperContext = GetTagHelperContext();
            var cacheTagHelper = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext(),
                VaryByRoute = varyByRoute
            };
            cacheTagHelper.ViewContext.RouteData.Values["id"] = 4;
            cacheTagHelper.ViewContext.RouteData.Values["category"] = "MyCategory";

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #10
0
        public void GenerateKey_UsesVaryByQuery(string varyByQuery, string expected)
        {
            // Arrange
            var tagHelperContext = GetTagHelperContext();
            var cacheTagHelper = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext(),
                VaryByQuery = varyByQuery
            };
            cacheTagHelper.ViewContext.HttpContext.Request.QueryString =
                new QueryString("?sortoption=Adorability&Category=cats&sortOrder=");

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #11
0
        public void GenerateKey_UsesVaryByPropertyToGenerateKey(string varyBy)
        {
            // Arrange
            var tagHelperContext = GetTagHelperContext();
            var cacheTagHelper = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext(),
                VaryBy = varyBy
            };
            var expected = "CacheTagHelper||testid||VaryBy||" + varyBy;

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #12
0
        public void GenerateKey_UsesVaryByHeader(string varyByHeader, string expected)
        {
            // Arrange
            var tagHelperContext = GetTagHelperContext();
            var cacheTagHelper = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext(),
                VaryByHeader = varyByHeader
            };
            var headers = cacheTagHelper.ViewContext.HttpContext.Request.Headers;
            headers["Accept-Language"] = "en-us;charset=utf8";
            headers["Accept-Encoding"] = "utf8";
            headers["X-CustomHeader"] = "Header-Value";

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #13
0
        public void GenerateKey_UsesVaryByCookieName(string varyByCookie, string expected)
        {
            // Arrange
            var tagHelperContext = GetTagHelperContext();
            var cacheTagHelper = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext(),
                VaryByCookie = varyByCookie
            };
            cacheTagHelper.ViewContext.HttpContext.Request.Headers["Cookie"] =
                "Cookie0=Cookie0Value;Cookie1=Cookie1Value";

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #14
0
        public void GenerateKey_ReturnsKeyBasedOnTagHelperUniqueId()
        {
            // Arrange
            var id = Guid.NewGuid().ToString();
            var tagHelperContext = GetTagHelperContext(id);
            var cacheTagHelper = new CacheTagHelper(Mock.Of<IMemoryCache>(), new HtmlTestEncoder())
            {
                ViewContext = GetViewContext()
            };
            var expected = "CacheTagHelper||" + id;

            // Act
            var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext);
            var key = cacheTagKey.GenerateKey();

            // Assert
            Assert.Equal(expected, key);
        }
Beispiel #15
0
        /// <inheritdoc />
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (output == null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            IHtmlContent content = null;

            if (Enabled)
            {
                var cacheKey = new CacheTagKey(this, context);

                MemoryCacheEntryOptions options;

                while (content == null)
                {
                    Task<IHtmlContent> result = null;

                    if (!MemoryCache.TryGetValue(cacheKey, out result))
                    {
                        var tokenSource = new CancellationTokenSource();

                        // Create an entry link scope and flow it so that any tokens related to the cache entries
                        // created within this scope get copied to this scope.

                        options = GetMemoryCacheEntryOptions();
                        options.AddExpirationToken(new CancellationChangeToken(tokenSource.Token));

                        var tcs = new TaskCompletionSource<IHtmlContent>();

                        // The returned value is ignored, we only do this so that
                        // the compiler doesn't complain about the returned task
                        // not being awaited
                        var localTcs = MemoryCache.Set(cacheKey, tcs.Task, options);

                        try
                        {
                            // The entry is set instead of assigning a value to the 
                            // task so that the expiration options are are not impacted 
                            // by the time it took to compute it.

                            using (var entry = MemoryCache.CreateEntry(cacheKey))
                            {
                                // The result is processed inside an entry
                                // such that the tokens are inherited.

                                result = ProcessContentAsync(output);
                                content = await result;

                                entry.SetOptions(options);
                                entry.Value = result;
                            }
                        }
                        catch
                        {
                            // Remove the worker task from the cache in case it can't complete.
                            tokenSource.Cancel();
                            throw;
                        }
                        finally
                        {
                            // If an exception occurs, ensure the other awaiters 
                            // render the output by themselves.
                            tcs.SetResult(null);
                        }
                    }
                    else
                    {
                        // There is either some value already cached (as a Task)
                        // or a worker processing the output. In the case of a worker,
                        // the result will be null, and the request will try to acquire
                        // the result from memory another time.

                        content = await result;
                    }
                }
            }
            else
            {
                content = await output.GetChildContentAsync();
            }

            // Clear the contents of the "cache" element since we don't want to render it.
            output.SuppressOutput();

            output.Content.SetHtmlContent(content);
        }