Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Components/Authorization/src/AuthorizeViewCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Microsoft.AspNetCore.Components.Authorization;
/// <summary>
/// A base class for components that display differing content depending on the user's authorization status.
/// </summary>
[CacheBoundaryPolicy(Excluded = true, VaryBy = CacheBoundaryVaryBy.User)]
public abstract class AuthorizeViewCore : ComponentBase
{
private AuthenticationState? currentAuthenticationState;
Expand Down
29 changes: 29 additions & 0 deletions src/Components/Components/src/CacheBoundaryPolicyAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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 <see cref="Excluded"/> is <see langword="true"/>, the component's subtree becomes
/// a "hole" in the cached output and is re-rendered on every request.
/// Optionally, set <see cref="VaryBy"/> to lift the exclusion when the cache boundary
/// varies by the specified dimensions.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class CacheBoundaryPolicyAttribute : Attribute
{
/// <summary>
/// Gets or sets a value indicating whether the component should be excluded
/// from cached output. Defaults to <see langword="false"/>.
/// </summary>
public bool Excluded { get; set; }

/// <summary>
/// Gets or sets the vary-by dimensions that, when active on the enclosing
/// CacheBoundary, lift the exclusion. The component is only excluded when
/// the cache boundary does <em>not</em> vary by all specified dimensions.
/// Defaults to <see cref="CacheBoundaryVaryBy.None"/>.
/// </summary>
public CacheBoundaryVaryBy VaryBy { get; set; }
}
48 changes: 48 additions & 0 deletions src/Components/Components/src/CacheBoundaryVaryBy.cs
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,

/// <summary>
/// Vary by culture.
/// </summary>
Culture = 1 << 5,
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

<ItemGroup>
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="RenderTree" />
<Compile Include="$(ComponentsSharedSourceRoot)src\ComponentKeyHelper.cs" LinkBase="Shared" />
<Compile Include="$(ComponentsSharedSourceRoot)src\JsonSerializerOptionsProvider.cs" />
<Compile Include="$(ComponentsSharedSourceRoot)src\HotReloadManager.cs" LinkBase="HotReload" />
<Compile Include="$(ComponentsSharedSourceRoot)src\RootTypeCache.cs" LinkBase="Shared" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,8 @@ private static string ComputeFinalKey(byte[] preKey, ComponentState componentSta

private static ReadOnlySpan<char> ResolveKeySpan(object? key)
{
if (key is IFormattable formattable)
{
var keyString = formattable.ToString("", CultureInfo.InvariantCulture);
return keyString.AsSpan();
}
else if (key is IConvertible convertible)
{
var keyString = convertible.ToString(CultureInfo.InvariantCulture);
return keyString.AsSpan();
}
return default;
var formatted = ComponentKeyHelper.FormatSerializableKey(key);
return formatted.AsSpan();
}

private static void GrowBuffer(ref byte[]? pool, ref Span<byte> keyBuffer, int? size = null)
Expand All @@ -154,7 +145,7 @@ private static void GrowBuffer(ref byte[]? pool, ref Span<byte> keyBuffer, int?
private static object? GetSerializableKey(ComponentState componentState)
{
var componentKey = componentState.GetComponentKey();
if (componentKey != null && IsSerializableKey(componentKey))
if (componentKey != null && ComponentKeyHelper.IsSerializableKey(componentKey))
{
return componentKey;
}
Expand Down Expand Up @@ -195,20 +186,4 @@ private static string GetParentComponentType(ComponentState componentState)

private static byte[] KeyFactory((string parentComponentType, string componentType, string propertyName) parts) =>
SHA256.HashData(Encoding.UTF8.GetBytes(string.Join(".", parts.parentComponentType, parts.componentType, parts.propertyName)));

private static bool IsSerializableKey(object key)
{
if (key == null)
{
return false;
}
var keyType = key.GetType();
var result = Type.GetTypeCode(keyType) != TypeCode.Object
|| keyType == typeof(Guid)
|| keyType == typeof(DateTimeOffset)
|| keyType == typeof(DateOnly)
|| keyType == typeof(TimeOnly);

return result;
}
}
14 changes: 14 additions & 0 deletions src/Components/Components/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
#nullable enable
Microsoft.AspNetCore.Components.CacheBoundaryPolicyAttribute
Microsoft.AspNetCore.Components.CacheBoundaryPolicyAttribute.CacheBoundaryPolicyAttribute() -> void
Microsoft.AspNetCore.Components.CacheBoundaryPolicyAttribute.Excluded.get -> bool
Microsoft.AspNetCore.Components.CacheBoundaryPolicyAttribute.Excluded.set -> void
Microsoft.AspNetCore.Components.CacheBoundaryPolicyAttribute.VaryBy.get -> Microsoft.AspNetCore.Components.CacheBoundaryVaryBy
Microsoft.AspNetCore.Components.CacheBoundaryPolicyAttribute.VaryBy.set -> void
Microsoft.AspNetCore.Components.CacheBoundaryVaryBy
Microsoft.AspNetCore.Components.CacheBoundaryVaryBy.Cookie = 8 -> Microsoft.AspNetCore.Components.CacheBoundaryVaryBy
Microsoft.AspNetCore.Components.CacheBoundaryVaryBy.Culture = 32 -> Microsoft.AspNetCore.Components.CacheBoundaryVaryBy
Microsoft.AspNetCore.Components.CacheBoundaryVaryBy.Header = 4 -> Microsoft.AspNetCore.Components.CacheBoundaryVaryBy
Microsoft.AspNetCore.Components.CacheBoundaryVaryBy.None = 0 -> Microsoft.AspNetCore.Components.CacheBoundaryVaryBy
Microsoft.AspNetCore.Components.CacheBoundaryVaryBy.Query = 1 -> Microsoft.AspNetCore.Components.CacheBoundaryVaryBy
Microsoft.AspNetCore.Components.CacheBoundaryVaryBy.Route = 2 -> Microsoft.AspNetCore.Components.CacheBoundaryVaryBy
Microsoft.AspNetCore.Components.CacheBoundaryVaryBy.User = 16 -> Microsoft.AspNetCore.Components.CacheBoundaryVaryBy
abstract Microsoft.AspNetCore.Components.CascadingParameterSubscription.Dispose() -> void
abstract Microsoft.AspNetCore.Components.CascadingParameterSubscription.GetCurrentValue() -> object?
Microsoft.AspNetCore.Components.CascadingParameterSubscription
Expand Down
1 change: 1 addition & 0 deletions src/Components/Components/src/Sections/SectionContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.AspNetCore.Components.Sections;
/// <summary>
/// Provides content to <see cref="SectionOutlet"/> components with matching <see cref="SectionId"/>s.
/// </summary>
[CacheBoundaryPolicy(Excluded = true)]
public sealed class SectionContent : IComponent, IDisposable
{
private object? _registeredIdentifier;
Expand Down
1 change: 1 addition & 0 deletions src/Components/Components/src/Sections/SectionOutlet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Components.Sections;
/// <summary>
/// Renders content provided by <see cref="SectionContent"/> components with matching <see cref="SectionId"/>s.
/// </summary>
[CacheBoundaryPolicy(Excluded = true)]
public sealed class SectionOutlet : IComponent, IDisposable
{
private static readonly RenderFragment _emptyRenderFragment = _ => { };
Expand Down
Loading
Loading