-
Notifications
You must be signed in to change notification settings - Fork 10.7k
CacheBoundary support for Blazor #65772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dariatiurina
wants to merge
48
commits into
dotnet:main
Choose a base branch
from
dariatiurina:55520-cache
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
ace51a8
Implement CacheComponent for Blazor SSR output caching
dariatiurina 476c95b
Fixes
dariatiurina bde38ec
Improvements
dariatiurina 741aec2
Feedback
dariatiurina 130c214
Merge branch 'main' into 55520-cache
dariatiurina b55dcb2
Feedback fix
dariatiurina 45caec5
Clean-up
dariatiurina 714f48c
Fix bug with hole replay
dariatiurina 8a406a8
Fix rendermode bug
dariatiurina dfb4b28
Changed the way annotation works
dariatiurina 1f960b7
Merge branch 'main' into 55520-cache
dariatiurina 5473fa3
Merge branch 'main' into 55520-cache
dariatiurina 2f26484
Changed CacheBoundary to use RenderFragment serialization
dariatiurina 1ee7752
Fixes
dariatiurina c9381e9
Fix for QuickGrid
dariatiurina b3ff152
Clean-up
dariatiurina d66bb6c
Clean-up
dariatiurina eec2880
Fix hot reload
dariatiurina cdc47e9
Fix
dariatiurina 615059e
Fix to async lifecycle and add support for HybridCache
dariatiurina d51ff7f
Fixes
dariatiurina ffa790e
Clean-up
dariatiurina 47302c0
Clean-up
dariatiurina fb295d1
Added wildcard to VaryByQuery and sorting
dariatiurina 41b9d7b
Fix bug with cancellation
dariatiurina b4781b8
Throwing when not enabled
dariatiurina bde200c
Merge branch 'main' into 55520-cache
dariatiurina ac89d80
Fix deadlock
dariatiurina caf1bd7
E2E tests additions
dariatiurina e24ed6c
Revert tests
dariatiurina b35d087
Feedback
dariatiurina 6c8bc98
Added consistency with PersistentComponentState
dariatiurina 358808b
Fixed forms for cache
dariatiurina 295b64f
Throw when we have RF as parameter.
dariatiurina 65155eb
Refactoring
dariatiurina e2440d7
Service creation
dariatiurina b6eb166
Refactoring
dariatiurina ceb180b
Refactoring and clean-up
dariatiurina e422b9c
Clean-up
dariatiurina 223fc8f
Apply suggestions from code review
dariatiurina 0718353
Changed storage choice strategy
dariatiurina 2179b36
Fixed xml
dariatiurina 9fde434
Fix
dariatiurina 5ad0069
Fix HybridCache
dariatiurina 77f612d
Feedback
dariatiurina 39a3414
Fix flakyness
dariatiurina d9e6b95
Clean-up
dariatiurina e8b7a3b
Merge branch 'main' into 55520-cache
dariatiurina File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
src/Components/Components/src/CacheBoundaryPolicyAttribute.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Microsoft.AspNetCore.Components; | ||
|
|
||
| /// <summary> | ||
| /// Specifies how a component interacts with an enclosing CacheBoundary. | ||
| /// When present, the component is treated as a "hole" in the cached output: the | ||
| /// component is instantiated and executes its own lifecycle on every request rather | ||
| /// than being served from the cached HTML. Its parameters are captured at the time the | ||
| /// cache entry is produced and are replayed unchanged on subsequent requests, so values | ||
| /// closed over by the surrounding render remain those of the original render. | ||
| /// Optionally, set <see cref="VaryBy"/> to lift the exclusion when the cache boundary | ||
| /// varies by the specified dimensions, in which case the component is included in | ||
| /// the cached output like any other. | ||
| /// <para> | ||
| /// <see cref="RenderFragment"/> parameters are not supported on hole components, because the | ||
| /// hole re-renders on every request while its parameters are captured once and replayed; a | ||
| /// captured <see cref="RenderFragment"/> would be frozen to the content of the first render. | ||
| /// Encountering a hole with a <see cref="RenderFragment"/> parameter throws an | ||
| /// <see cref="InvalidOperationException"/>. To fix this, remove the <see cref="RenderFragment"/> | ||
| /// parameter or move the component outside the CacheBoundary. | ||
| /// </para> | ||
| /// </summary> | ||
| [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] | ||
| public sealed class CacheBoundaryPolicyAttribute : Attribute | ||
| { | ||
| /// <summary> | ||
| /// Gets or sets a value indicating whether encountering this component inside | ||
| /// a cache boundary is disallowed and should throw an <see cref="InvalidOperationException"/>. | ||
| /// Use this for components whose parameters (delegates, expressions, or complex | ||
| /// objects) would not behave correctly if captured once and replayed on later | ||
| /// requests; for example because they close over per-request state or | ||
| /// because the rendered output depends on values that change between requests. | ||
| /// The exception is suppressed when the enclosing CacheBoundary varies by all | ||
| /// of the dimensions specified by <see cref="VaryBy"/>. | ||
| /// Defaults to <see langword="false"/>. | ||
| /// </summary> | ||
| public bool Disallow { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the vary-by dimensions that, when all active on the enclosing | ||
| /// CacheBoundary, lift the exclusion. When lifted, the component participates | ||
| /// in the cached output like any other component (its captured parameters and | ||
| /// child content are stored in the cache entry and replayed on cache hits), | ||
| /// and the cache key already distinguishes the values of those dimensions so | ||
| /// the replay is correct. When not lifted, the component is treated as a hole | ||
| /// (or throws, when <see cref="Disallow"/> is <see langword="true"/>). | ||
| /// Defaults to <see cref="CacheBoundaryVaryBy.None"/>. | ||
| /// </summary> | ||
| public CacheBoundaryVaryBy VaryBy { get; set; } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Microsoft.AspNetCore.Components; | ||
|
|
||
| /// <summary> | ||
| /// Describes which vary-by dimensions are active on an enclosing CacheBoundary. | ||
| /// Used as flags in <see cref="CacheBoundaryPolicyAttribute"/> to express conditional | ||
| /// cache exclusion, and internally to communicate the active dimensions to the renderer. | ||
| /// </summary> | ||
| [Flags] | ||
| public enum CacheBoundaryVaryBy | ||
| { | ||
| /// <summary> | ||
| /// No vary-by dimensions. | ||
| /// </summary> | ||
| None = 0, | ||
|
|
||
| /// <summary> | ||
| /// Vary by query string parameters. | ||
| /// </summary> | ||
| Query = 1 << 0, | ||
|
|
||
| /// <summary> | ||
| /// Vary by route parameters. | ||
| /// </summary> | ||
| Route = 1 << 1, | ||
|
|
||
| /// <summary> | ||
| /// Vary by HTTP header values. | ||
| /// </summary> | ||
| Header = 1 << 2, | ||
|
|
||
| /// <summary> | ||
| /// Vary by cookie values. | ||
| /// </summary> | ||
| Cookie = 1 << 3, | ||
|
|
||
| /// <summary> | ||
| /// Vary by the authenticated user. | ||
| /// </summary> | ||
| User = 1 << 4, | ||
|
dariatiurina marked this conversation as resolved.
|
||
|
|
||
| /// <summary> | ||
| /// Vary by culture. | ||
| /// </summary> | ||
| Culture = 1 << 5, | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
151 changes: 151 additions & 0 deletions
151
src/Components/Endpoints/src/CacheBoundary/CacheBoundary.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.AspNetCore.Components.Endpoints; | ||
| using Microsoft.AspNetCore.Components.Rendering; | ||
| using Microsoft.AspNetCore.Http; | ||
| using Microsoft.Extensions.Caching.Memory; | ||
|
|
||
| namespace Microsoft.AspNetCore.Components; | ||
|
|
||
| /// <summary> | ||
| /// A component that caches the rendered HTML of its child content during | ||
| /// server-side rendering (SSR). On cache hit, child components are not | ||
| /// instantiated or rendered. | ||
| /// </summary> | ||
| public sealed class CacheBoundary : IComponent, IDisposable | ||
| { | ||
| private RenderHandle _renderHandle; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the content to be cached. | ||
| /// </summary> | ||
| [Parameter] | ||
| public RenderFragment? ChildContent { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets an explicit cache key for disambiguation when multiple | ||
| /// <see cref="CacheBoundary"/> instances share the same component ancestor. | ||
| /// </summary> | ||
| [Parameter] | ||
| public string? CacheKey { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets whether caching is enabled. Defaults to <c>true</c>. | ||
| /// </summary> | ||
| [Parameter] | ||
| public bool Enabled { get; set; } = true; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets how long after creation the cache entry should be evicted. | ||
| /// </summary> | ||
| [Parameter] | ||
| public TimeSpan? ExpiresAfter { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the absolute <see cref="DateTimeOffset"/> when the cache entry should be evicted. | ||
| /// </summary> | ||
| [Parameter] | ||
| public DateTimeOffset? ExpiresOn { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets how long after last access the cache entry should be evicted. | ||
| /// Not supported when the cache boundary store uses <c>HybridCache</c>. | ||
| /// </summary> | ||
| [Parameter] | ||
| public TimeSpan? ExpiresSliding { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the <see cref="CacheItemPriority"/> policy for the cache entry. | ||
| /// Not supported when the cache boundary store uses <c>HybridCache</c>. | ||
| /// </summary> | ||
| [Parameter] | ||
| public CacheItemPriority? Priority { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a comma-separated list of query string parameter names to vary the cache by. | ||
| /// Use <c>"*"</c> to vary by all query string parameters. | ||
| /// </summary> | ||
| [Parameter] | ||
| public string? VaryByQuery { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a comma-separated list of route parameter names to vary the cache by. | ||
| /// </summary> | ||
| [Parameter] | ||
| public string? VaryByRoute { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a comma-separated list of HTTP header names to vary the cache by. | ||
| /// </summary> | ||
| [Parameter] | ||
| public string? VaryByHeader { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a comma-separated list of cookie names to vary the cache by. | ||
| /// </summary> | ||
| [Parameter] | ||
| public string? VaryByCookie { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets whether to vary the cache by the authenticated user identity. | ||
| /// </summary> | ||
| [Parameter] | ||
| public bool? VaryByUser { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets whether to vary the cache by the current culture. | ||
| /// </summary> | ||
| [Parameter] | ||
| public bool? VaryByCulture { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a custom string value to vary the cache by. | ||
| /// </summary> | ||
| [Parameter] | ||
| public string? VaryBy { get; set; } | ||
|
|
||
| [Inject] internal CacheBoundaryService? CacheService { get; set; } | ||
| [CascadingParameter] internal HttpContext? HttpContext { get; set; } | ||
| internal Func<string>? TreePositionKeyFactory { get; set; } | ||
| internal string? TreePositionKey => TreePositionKeyFactory?.Invoke(); | ||
|
|
||
| internal bool IsInStreamingContext { get; set; } | ||
|
|
||
| // The per-render coordination state produced by CacheBoundaryService. Null when caching is inactive | ||
| // for this render; the renderer reads it to drive capture. | ||
| internal CacheBoundaryRenderState? RenderState { get; private set; } | ||
|
|
||
| /// <inheritdoc/> | ||
| void IComponent.Attach(RenderHandle renderHandle) | ||
| { | ||
| _renderHandle = renderHandle; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| async Task IComponent.SetParametersAsync(ParameterView parameters) | ||
| { | ||
| parameters.SetParameterProperties(this); | ||
|
|
||
| RenderState = HttpContext is { } httpContext && CacheService is { } cacheService | ||
| ? await cacheService.PrepareAsync(this, httpContext) | ||
| : null; | ||
|
|
||
| _renderHandle.Render(BuildRenderTree); | ||
| } | ||
|
|
||
| private void BuildRenderTree(RenderTreeBuilder builder) | ||
| { | ||
| var content = RenderState?.Content ?? ChildContent; | ||
| content?.Invoke(builder); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public void Dispose() | ||
| { | ||
| if (RenderState is { } state) | ||
| { | ||
| CacheBoundaryService.OnBoundaryDisposed(state); | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we shorten the xml? Let's take a look at other embedded docs length for attributes:
https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/CascadingParameterAttribute.cs
https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/StreamRenderingAttribute.cs
https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/EventHandlerAttribute.cs
While it's good to have understandable xmls, detailed != clear. Let's try to stick to 5-10 lines of docs in this file.