From dc3efc4d625e4d9646231a9fd20e727092079bd6 Mon Sep 17 00:00:00 2001 From: MattyLeslie Date: Tue, 28 May 2024 12:00:31 +0200 Subject: [PATCH 1/5] Supporting DateOnly and TimeOnly and hooking into the hot reload notification --- .../Components/src/ChangeDetection.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Components/Components/src/ChangeDetection.cs b/src/Components/Components/src/ChangeDetection.cs index 68918d023758..3c5fd0a05cc5 100644 --- a/src/Components/Components/src/ChangeDetection.cs +++ b/src/Components/Components/src/ChangeDetection.cs @@ -1,10 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Concurrent; + namespace Microsoft.AspNetCore.Components; internal sealed class ChangeDetection { + private static ConcurrentDictionary _immutableNonObjectTypesCache = new(); public static bool MayHaveChanged(T1 oldValue, T2 newValue) { var oldIsNotNull = oldValue != null; @@ -41,6 +44,24 @@ public static bool MayHaveChanged(T1 oldValue, T2 newValue) // For performance reasons, the following immutable types are not supported: IntPtr, UIntPtr, Type. private static bool IsKnownImmutableType(Type type) => Type.GetTypeCode(type) != TypeCode.Object - || type == typeof(Guid) + || _immutableNonObjectTypesCache.GetOrAdd(type, IsImmutableNonObjectCore); + + private static bool IsImmutableNonObjectCore(Type type) + => type == typeof(Guid) + || type == typeof(DateOnly) + || type == typeof(TimeOnly) || typeof(IEventCallback).IsAssignableFrom(type); + + public static event Action? OnHotReload; + + public static void ClearImmutableTypeCache() + { + _immutableNonObjectTypesCache.Clear(); + } + + public static void HotReloadHandler() + { + ClearImmutableTypeCache(); + OnHotReload?.Invoke(); + } } From a165d3376bade47ffdcc11039a96b9a6dc5e5cf5 Mon Sep 17 00:00:00 2001 From: MattyLeslie Date: Tue, 28 May 2024 12:01:05 +0200 Subject: [PATCH 2/5] Updating "SkipsUpdatingParametersOnChildComponentsIfAllAreDefinitelyImmutableAndUnchanged" test to account for the new behaviours of DateOnly and TimeOnly. --- src/Components/Components/test/RenderTreeDiffBuilderTest.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Components/Components/test/RenderTreeDiffBuilderTest.cs b/src/Components/Components/test/RenderTreeDiffBuilderTest.cs index aebcfcd4b692..4d495f690b47 100644 --- a/src/Components/Components/test/RenderTreeDiffBuilderTest.cs +++ b/src/Components/Components/test/RenderTreeDiffBuilderTest.cs @@ -1736,6 +1736,9 @@ public void SkipsUpdatingParametersOnChildComponentsIfAllAreDefinitelyImmutableA // Arrange: Populate old and new with equivalent content RenderFragment fragmentWillNotChange = builder => throw new NotImplementedException(); var dateTimeWillNotChange = DateTime.Now; + var dateOnlyWillNotChange = DateOnly.FromDateTime(dateTimeWillNotChange); + var timeOnlyWillNotChange = TimeOnly.FromDateTime(dateTimeWillNotChange); + foreach (var tree in new[] { oldTree, newTree }) { tree.OpenComponent(0); @@ -1758,6 +1761,8 @@ public void SkipsUpdatingParametersOnChildComponentsIfAllAreDefinitelyImmutableA tree.AddComponentParameter(1, "MyEnum", StringComparison.OrdinalIgnoreCase); tree.AddComponentParameter(1, "MyEventCallback", EventCallback.Empty); tree.AddComponentParameter(1, "MyEventCallbackOfT", EventCallback.Empty); + tree.AddComponentParameter(1, "MyDateOnly", dateOnlyWillNotChange); + tree.AddComponentParameter(1, "MyTimeOnly", timeOnlyWillNotChange); tree.CloseComponent(); } From 3fcb59e204a074f71c1e3eccd20081a36a9ae9ca Mon Sep 17 00:00:00 2001 From: MattyLeslie Date: Tue, 28 May 2024 12:29:17 +0200 Subject: [PATCH 3/5] Making _immutableNonObjectTypesCache readonly --- src/Components/Components/src/ChangeDetection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Components/src/ChangeDetection.cs b/src/Components/Components/src/ChangeDetection.cs index 3c5fd0a05cc5..3a1b44765fe8 100644 --- a/src/Components/Components/src/ChangeDetection.cs +++ b/src/Components/Components/src/ChangeDetection.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Components; internal sealed class ChangeDetection { - private static ConcurrentDictionary _immutableNonObjectTypesCache = new(); + private static readonly ConcurrentDictionary _immutableNonObjectTypesCache = new(); public static bool MayHaveChanged(T1 oldValue, T2 newValue) { var oldIsNotNull = oldValue != null; From 37c258435508d0f68f73dff3988d7e3a3f0f4fd6 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Mon, 21 Oct 2024 11:32:19 -0700 Subject: [PATCH 4/5] Update src/Components/Components/src/ChangeDetection.cs --- src/Components/Components/src/ChangeDetection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Components/Components/src/ChangeDetection.cs b/src/Components/Components/src/ChangeDetection.cs index 3a1b44765fe8..e02caeb8a9f2 100644 --- a/src/Components/Components/src/ChangeDetection.cs +++ b/src/Components/Components/src/ChangeDetection.cs @@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Components; internal sealed class ChangeDetection { private static readonly ConcurrentDictionary _immutableNonObjectTypesCache = new(); + public static bool MayHaveChanged(T1 oldValue, T2 newValue) { var oldIsNotNull = oldValue != null; From 77fd269ceb76225825622acab5286d1c9cde7f8a Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Mon, 21 Oct 2024 11:57:51 -0700 Subject: [PATCH 5/5] Suggestions from code review --- .../Components/src/ChangeDetection.cs | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Components/Components/src/ChangeDetection.cs b/src/Components/Components/src/ChangeDetection.cs index e02caeb8a9f2..952debeb8f90 100644 --- a/src/Components/Components/src/ChangeDetection.cs +++ b/src/Components/Components/src/ChangeDetection.cs @@ -2,12 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Concurrent; +using Microsoft.AspNetCore.Components.HotReload; namespace Microsoft.AspNetCore.Components; internal sealed class ChangeDetection { - private static readonly ConcurrentDictionary _immutableNonObjectTypesCache = new(); + private static readonly ConcurrentDictionary _immutableObjectTypesCache = new(); + + static ChangeDetection() + { + if (HotReloadManager.Default.MetadataUpdateSupported) + { + HotReloadManager.Default.OnDeltaApplied += _immutableObjectTypesCache.Clear; + } + } public static bool MayHaveChanged(T1 oldValue, T2 newValue) { @@ -34,10 +43,6 @@ public static bool MayHaveChanged(T1 oldValue, T2 newValue) return false; } - // The contents of this list need to trade off false negatives against computation - // time. So we don't want a huge list of types to check (or would have to move to - // a hashtable lookup, which is differently expensive). It's better not to include - // uncommon types here even if they are known to be immutable. // This logic assumes that no new System.TypeCode enum entries have been declared since 7.0, or at least that any new ones // represent immutable types. If System.TypeCode changes, review this logic to ensure it is still correct. // Supported immutable types : bool, byte, sbyte, short, ushort, int, uint, long, ulong, char, double, @@ -45,24 +50,11 @@ public static bool MayHaveChanged(T1 oldValue, T2 newValue) // For performance reasons, the following immutable types are not supported: IntPtr, UIntPtr, Type. private static bool IsKnownImmutableType(Type type) => Type.GetTypeCode(type) != TypeCode.Object - || _immutableNonObjectTypesCache.GetOrAdd(type, IsImmutableNonObjectCore); + || _immutableObjectTypesCache.GetOrAdd(type, IsImmutableObjectTypeCore); - private static bool IsImmutableNonObjectCore(Type type) + private static bool IsImmutableObjectTypeCore(Type type) => type == typeof(Guid) || type == typeof(DateOnly) || type == typeof(TimeOnly) || typeof(IEventCallback).IsAssignableFrom(type); - - public static event Action? OnHotReload; - - public static void ClearImmutableTypeCache() - { - _immutableNonObjectTypesCache.Clear(); - } - - public static void HotReloadHandler() - { - ClearImmutableTypeCache(); - OnHotReload?.Invoke(); - } }