From 7bd65cffbf4872b0273700509e8d0f55500793f0 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 7 May 2021 14:55:30 -0700 Subject: [PATCH 1/3] Use feature switch to perform hot reload trimming Contributes to https://github.com/dotnet/runtime/issues/51159 --- .../Microsoft.AspNetCore.Components.csproj | 2 +- .../src/Properties/ILLink.Substitutions.xml | 14 ++--- src/Components/Components/src/RenderHandle.cs | 3 +- .../src/RenderTree/RenderTreeDiffBuilder.cs | 3 +- .../Components/src/RenderTree/Renderer.cs | 53 +++++-------------- .../Shared/src/HotReloadEnvironment.cs | 22 -------- src/Components/Shared/src/HotReloadFeature.cs | 15 ++++++ .../src/Hosting/WebAssemblyHost.cs | 13 ++--- .../src/HotReload/WebAssemblyHotReload.cs | 7 +-- ...t.AspNetCore.Components.WebAssembly.csproj | 4 +- .../src/Properties/ILLink.Substitutions.xml | 5 +- .../testassets/TestServer/HotReloadStartup.cs | 2 - 12 files changed, 48 insertions(+), 95 deletions(-) delete mode 100644 src/Components/Shared/src/HotReloadEnvironment.cs create mode 100644 src/Components/Shared/src/HotReloadFeature.cs diff --git a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj index 490ee213ed6b..9a75de9cca0d 100644 --- a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj +++ b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/Components/Components/src/Properties/ILLink.Substitutions.xml b/src/Components/Components/src/Properties/ILLink.Substitutions.xml index 724381711cb7..90abb328b06f 100644 --- a/src/Components/Components/src/Properties/ILLink.Substitutions.xml +++ b/src/Components/Components/src/Properties/ILLink.Substitutions.xml @@ -1,17 +1,11 @@ - - - - - - - - - - + + + + diff --git a/src/Components/Components/src/RenderHandle.cs b/src/Components/Components/src/RenderHandle.cs index 3b6cf7de5290..d7789e18cec8 100644 --- a/src/Components/Components/src/RenderHandle.cs +++ b/src/Components/Components/src/RenderHandle.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Components.HotReload; using Microsoft.AspNetCore.Components.RenderTree; namespace Microsoft.AspNetCore.Components @@ -46,7 +47,7 @@ public Dispatcher Dispatcher /// /// Gets a value that determines if the is triggering a render in response to a hot-reload change. /// - public bool IsHotReloading => _renderer?.IsHotReloading ?? false; + public bool IsHotReloading => HotReloadFeature.IsSupported && (_renderer?.IsHotReloading ?? false); /// /// Notifies the renderer that the component should be rendered. diff --git a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs index 5669fdad80fb..6ba718884410 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Components.HotReload; using Microsoft.AspNetCore.Components.Rendering; namespace Microsoft.AspNetCore.Components.RenderTree @@ -541,7 +542,7 @@ private static void UpdateRetainedChildComponent( var oldParameters = new ParameterView(ParameterViewLifetime.Unbound, oldTree, oldComponentIndex); var newParametersLifetime = new ParameterViewLifetime(diffContext.BatchBuilder); var newParameters = new ParameterView(newParametersLifetime, newTree, newComponentIndex); - if (!newParameters.DefinitelyEquals(oldParameters) || diffContext.Renderer.IsHotReloading) + if (!newParameters.DefinitelyEquals(oldParameters) || (HotReloadFeature.IsSupported && diffContext.Renderer.IsHotReloading)) { componentState.SetDirectParameters(newParameters); } diff --git a/src/Components/Components/src/RenderTree/Renderer.cs b/src/Components/Components/src/RenderTree/Renderer.cs index 06e1724b7b5c..b059b1998f20 100644 --- a/src/Components/Components/src/RenderTree/Renderer.cs +++ b/src/Components/Components/src/RenderTree/Renderer.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.HotReload; @@ -35,10 +34,9 @@ public abstract partial class Renderer : IDisposable, IAsyncDisposable private readonly Dictionary _eventHandlerIdReplacements = new Dictionary(); private readonly ILogger _logger; private readonly ComponentFactory _componentFactory; - private HotReloadEnvironment? _hotReloadEnvironment; private List<(ComponentState, ParameterView)>? _rootComponents; - private int _nextComponentId = 0; // TODO: change to 'long' when Mono .NET->JS interop supports it + private int _nextComponentId = 0; private bool _isBatchInProgress; private ulong _lastEventHandlerId; private List? _pendingTasks; @@ -98,16 +96,7 @@ public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, _logger = loggerFactory.CreateLogger(); _componentFactory = new ComponentFactory(componentActivator); - InitializeHotReload(serviceProvider); - } - - private void InitializeHotReload(IServiceProvider serviceProvider) - { - // HotReloadEnvironment is a test-specific feature and may not be available in a running app. We'll fallback to the default instance - // if the test fixture does not provide one. - _hotReloadEnvironment = serviceProvider.GetService() ?? HotReloadEnvironment.Instance; - - if (_hotReloadEnvironment.IsHotReloadEnabled) + if (HotReloadFeature.IsSupported) { HotReloadManager.OnDeltaApplied += RenderRootComponentsOnHotReload; } @@ -232,7 +221,13 @@ protected async Task RenderRootComponentAsync(int componentId, ParameterView ini // During the asynchronous rendering process we want to wait up until all components have // finished rendering so that we can produce the complete output. var componentState = GetRequiredComponentState(componentId); - CaptureRootComponentForHotReload(initialParameters, componentState); + if (HotReloadFeature.IsSupported) + { + // when we're doing hot-reload, stash away the parameters used while rendering root components. + // We'll use this to trigger re-renders on hot reload updates. + _rootComponents ??= new(); + _rootComponents.Add((componentState, initialParameters.Clone())); + } componentState.SetDirectParameters(initialParameters); @@ -247,20 +242,6 @@ protected async Task RenderRootComponentAsync(int componentId, ParameterView ini } } - /// - /// Intentionally authored as a separate method call so we can trim this code. - /// - private void CaptureRootComponentForHotReload(ParameterView initialParameters, ComponentState componentState) - { - if (_hotReloadEnvironment?.IsHotReloadEnabled ?? false) - { - // when we're doing hot-reload, stash away the parameters used while rendering root components. - // We'll use this to trigger re-renders on hot reload updates. - _rootComponents ??= new(); - _rootComponents.Add((componentState, initialParameters.Clone())); - } - } - /// /// Allows derived types to handle exceptions during rendering. Defaults to rethrowing the original exception. /// @@ -1011,7 +992,10 @@ public void Dispose() /// public async ValueTask DisposeAsync() { - DisposeForHotReload(); + if (HotReloadFeature.IsSupported) + { + HotReloadManager.OnDeltaApplied -= RenderRootComponentsOnHotReload; + } if (_disposed) { @@ -1035,16 +1019,5 @@ public async ValueTask DisposeAsync() } } } - - /// - /// Intentionally authored as a separate method call so we can trim this code. - /// - private void DisposeForHotReload() - { - if (_hotReloadEnvironment?.IsHotReloadEnabled ?? false) - { - HotReloadManager.OnDeltaApplied -= RenderRootComponentsOnHotReload; - } - } } } diff --git a/src/Components/Shared/src/HotReloadEnvironment.cs b/src/Components/Shared/src/HotReloadEnvironment.cs deleted file mode 100644 index fef2cd9255cd..000000000000 --- a/src/Components/Shared/src/HotReloadEnvironment.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.AspNetCore.Components.HotReload -{ - internal class HotReloadEnvironment - { - public static readonly HotReloadEnvironment Instance = new(Environment.GetEnvironmentVariable("DOTNET_MODIFIABLE_ASSEMBLIES") == "debug"); - - public HotReloadEnvironment(bool isHotReloadEnabled) - { - IsHotReloadEnabled = isHotReloadEnabled; - } - - /// - /// Gets a value that determines if HotReload is configured for this application. - /// - public bool IsHotReloadEnabled { get; } - } -} diff --git a/src/Components/Shared/src/HotReloadFeature.cs b/src/Components/Shared/src/HotReloadFeature.cs new file mode 100644 index 000000000000..7a5ef6baa6b2 --- /dev/null +++ b/src/Components/Shared/src/HotReloadFeature.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Components.HotReload +{ + internal static class HotReloadFeature + { + /// + /// Gets a value that determines if hot reload is supported. Currently, the Debugger.IsSupported feature switch is used as a proxy for this. + /// + public static bool IsSupported { get; } = AppContext.TryGetSwitch("System.Diagnostics.Debugger.IsSupported", out var isSupported) ? isSupported : true; + } +} diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs index a2c7f8c0777c..70bfa2456cfd 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs @@ -4,6 +4,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.HotReload; using Microsoft.AspNetCore.Components.Lifetime; using Microsoft.AspNetCore.Components.WebAssembly.HotReload; using Microsoft.AspNetCore.Components.WebAssembly.Infrastructure; @@ -144,11 +145,9 @@ internal async Task RunAsyncCore(CancellationToken cancellationToken, WebAssembl await manager.RestoreStateAsync(store); - var initializeTask = InitializeHotReloadAsync(); - if (initializeTask is not null) + if (HotReloadFeature.IsSupported) { - // The returned value will be "null" in a trimmed app - await initializeTask; + await WebAssemblyHotReload.InitializeAsync(); } var tcs = new TaskCompletionSource(); @@ -184,11 +183,5 @@ internal async Task RunAsyncCore(CancellationToken cancellationToken, WebAssembl await tcs.Task; } } - - private Task? InitializeHotReloadAsync() - { - // In Development scenarios, wait for hot reload to apply deltas before initiating rendering. - return WebAssemblyHotReload.InitializeAsync(); - } } } diff --git a/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs b/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs index ba672d4c8d3d..c79182917fbc 100644 --- a/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs +++ b/src/Components/WebAssembly/WebAssembly/src/HotReload/WebAssemblyHotReload.cs @@ -5,7 +5,6 @@ using System.ComponentModel; using System.Diagnostics; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components.HotReload; using Microsoft.AspNetCore.Components.WebAssembly.Services; using Microsoft.Extensions.HotReload; using Microsoft.JSInterop; @@ -27,7 +26,10 @@ public static class WebAssemblyHotReload internal static async Task InitializeAsync() { - if (!HotReloadEnvironment.Instance.IsHotReloadEnabled) + // Determine if we're running under a hot reload environment (e.g. dotnet-watch). + // It's insufficient to know it the app can be hot reloaded (HotReloadEnvironment.IsEnabled), + // since the hot-reload agent might be unavilable. + if (Environment.GetEnvironmentVariable("DOTNET_MODIFIABLE_ASSEMBLIES") != "debug") { return; } @@ -45,7 +47,6 @@ internal static async Task InitializeAsync() public static void ApplyHotReloadDelta(string moduleIdString, byte[] metadataDelta, byte[] ilDeta) { var moduleId = Guid.Parse(moduleIdString); - Debug.Assert(HotReloadEnvironment.Instance.IsHotReloadEnabled); _updateDeltas[0].ModuleId = moduleId; _updateDeltas[0].MetadataDelta = metadataDelta; diff --git a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj index 46238a9a4c0d..f80c2e225fa5 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj +++ b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj @@ -1,4 +1,4 @@ - + $(DefaultNetCoreTargetFramework) @@ -32,7 +32,7 @@ - + diff --git a/src/Components/WebAssembly/WebAssembly/src/Properties/ILLink.Substitutions.xml b/src/Components/WebAssembly/WebAssembly/src/Properties/ILLink.Substitutions.xml index 95ac01acc9ac..2b8260e080d5 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Properties/ILLink.Substitutions.xml +++ b/src/Components/WebAssembly/WebAssembly/src/Properties/ILLink.Substitutions.xml @@ -1,8 +1,7 @@ - - - + + diff --git a/src/Components/test/testassets/TestServer/HotReloadStartup.cs b/src/Components/test/testassets/TestServer/HotReloadStartup.cs index f9b0061c9d41..aeb99355c4f1 100644 --- a/src/Components/test/testassets/TestServer/HotReloadStartup.cs +++ b/src/Components/test/testassets/TestServer/HotReloadStartup.cs @@ -3,7 +3,6 @@ using System.Globalization; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Components.HotReload; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -14,7 +13,6 @@ public class HotReloadStartup { public void ConfigureServices(IServiceCollection services) { - services.AddSingleton(new HotReloadEnvironment(isHotReloadEnabled: true)); services.AddControllers(); services.AddRazorPages(); services.AddServerSideBlazor(); From 067f5b538ac171c88df80b1f6b5848835ddb0b11 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 7 May 2021 15:54:36 -0700 Subject: [PATCH 2/3] Clear caches from more places on a hot reload event --- .../Components/src/ComponentFactory.cs | 4 ++- ...NetCore.Components.WarningSuppressions.xml | 2 +- .../src/Reflection/ComponentProperties.cs | 2 ++ .../Components/src/RenderTree/Renderer.cs | 5 ++++ .../src/Routing/RouteTableFactory.cs | 2 ++ .../Components/src/Routing/Router.cs | 16 ++++++++++++ .../EditContextDataAnnotationsExtensions.cs | 26 +++++++++++++++++++ ...crosoft.AspNetCore.Components.Forms.csproj | 11 ++++++++ .../src/Properties/ILLink.Substitutions.xml | 7 +++++ .../src/Infrastructure/DotNetDispatcher.cs | 9 +++++++ 10 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/Components/Forms/src/Properties/ILLink.Substitutions.xml diff --git a/src/Components/Components/src/ComponentFactory.cs b/src/Components/Components/src/ComponentFactory.cs index 49b3a6a95b3a..c3235a2be405 100644 --- a/src/Components/Components/src/ComponentFactory.cs +++ b/src/Components/Components/src/ComponentFactory.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Components { - internal class ComponentFactory + internal sealed class ComponentFactory { private static readonly BindingFlags _injectablePropertyBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; @@ -25,6 +25,8 @@ public ComponentFactory(IComponentActivator componentActivator) _componentActivator = componentActivator ?? throw new ArgumentNullException(nameof(componentActivator)); } + public void ClearCache() => _cachedInitializers.Clear(); + public IComponent InstantiateComponent(IServiceProvider serviceProvider, [DynamicallyAccessedMembers(Component)] Type componentType) { var component = _componentActivator.CreateInstance(componentType); diff --git a/src/Components/Components/src/Microsoft.AspNetCore.Components.WarningSuppressions.xml b/src/Components/Components/src/Microsoft.AspNetCore.Components.WarningSuppressions.xml index 7a1510c2300d..cc79f6d37717 100644 --- a/src/Components/Components/src/Microsoft.AspNetCore.Components.WarningSuppressions.xml +++ b/src/Components/Components/src/Microsoft.AspNetCore.Components.WarningSuppressions.xml @@ -17,7 +17,7 @@ ILLink IL2026 member - M:Microsoft.AspNetCore.Components.RouteTableFactory.<GetRouteableComponents>g__GetRouteableComponents|3_0(System.Collections.Generic.List{System.Type},System.Reflection.Assembly) + M:Microsoft.AspNetCore.Components.RouteTableFactory.<GetRouteableComponents>g__GetRouteableComponents|4_0(System.Collections.Generic.List{System.Type},System.Reflection.Assembly) ILLink diff --git a/src/Components/Components/src/Reflection/ComponentProperties.cs b/src/Components/Components/src/Reflection/ComponentProperties.cs index bab5d9d62957..0f45353abc63 100644 --- a/src/Components/Components/src/Reflection/ComponentProperties.cs +++ b/src/Components/Components/src/Reflection/ComponentProperties.cs @@ -20,6 +20,8 @@ internal static class ComponentProperties private readonly static ConcurrentDictionary _cachedWritersByType = new ConcurrentDictionary(); + public static void ClearCache() => _cachedWritersByType.Clear(); + public static void SetProperties(in ParameterView parameters, object target) { if (target == null) diff --git a/src/Components/Components/src/RenderTree/Renderer.cs b/src/Components/Components/src/RenderTree/Renderer.cs index b059b1998f20..fdc9b32d6210 100644 --- a/src/Components/Components/src/RenderTree/Renderer.cs +++ b/src/Components/Components/src/RenderTree/Renderer.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.HotReload; +using Microsoft.AspNetCore.Components.Reflection; using Microsoft.AspNetCore.Components.Rendering; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -126,6 +127,10 @@ private static IComponentActivator GetComponentActivatorOrDefault(IServiceProvid private async void RenderRootComponentsOnHotReload() { + // Before re-rendering the root component, also clear any well-known caches in the framework + _componentFactory.ClearCache(); + ComponentProperties.ClearCache(); + await Dispatcher.InvokeAsync(() => { if (_rootComponents is null) diff --git a/src/Components/Components/src/Routing/RouteTableFactory.cs b/src/Components/Components/src/Routing/RouteTableFactory.cs index e2ecd0edb7b1..e8f263fd08b8 100644 --- a/src/Components/Components/src/Routing/RouteTableFactory.cs +++ b/src/Components/Components/src/Routing/RouteTableFactory.cs @@ -30,6 +30,8 @@ public static RouteTable Create(RouteKey routeKey) return routeTable; } + public static void ClearCaches() => Cache.Clear(); + private static List GetRouteableComponents(RouteKey routeKey) { var routeableComponents = new List(); diff --git a/src/Components/Components/src/Routing/Router.cs b/src/Components/Components/src/Routing/Router.cs index 320403015858..91e89bbe86fe 100644 --- a/src/Components/Components/src/Routing/Router.cs +++ b/src/Components/Components/src/Routing/Router.cs @@ -9,6 +9,7 @@ using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.HotReload; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Components.Routing @@ -91,6 +92,11 @@ public void Attach(RenderHandle renderHandle) _baseUri = NavigationManager.BaseUri; _locationAbsolute = NavigationManager.Uri; NavigationManager.LocationChanged += OnLocationChanged; + + if (HotReloadFeature.IsSupported) + { + HotReloadManager.OnDeltaApplied += ClearRouteCaches; + } } /// @@ -131,6 +137,10 @@ public async Task SetParametersAsync(ParameterView parameters) public void Dispose() { NavigationManager.LocationChanged -= OnLocationChanged; + if (HotReloadFeature.IsSupported) + { + HotReloadManager.OnDeltaApplied -= ClearRouteCaches; + } } private static string StringUntilAny(string str, char[] chars) @@ -152,6 +162,12 @@ private void RefreshRouteTable() } } + private void ClearRouteCaches() + { + RouteTableFactory.ClearCaches(); + _currentRouteKey = default; + } + internal virtual void Refresh(bool isNavigationIntercepted) { // If an `OnNavigateAsync` task is currently in progress, then wait diff --git a/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs b/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs index a88060552c8c..ff0632370c92 100644 --- a/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs +++ b/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs @@ -7,7 +7,11 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Reflection.Metadata; using System.Runtime.InteropServices; +using Microsoft.AspNetCore.Components.HotReload; + +[assembly: MetadataUpdateHandler(typeof(Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions))] namespace Microsoft.AspNetCore.Components.Forms { @@ -37,6 +41,13 @@ public static IDisposable EnableDataAnnotationsValidation(this EditContext editC return new DataAnnotationsEventSubscriptions(editContext); } + private static event Action? OnClearCache; + + private static void ClearCache(Type[]? _) + { + OnClearCache?.Invoke(); + } + private sealed class DataAnnotationsEventSubscriptions : IDisposable { private static readonly ConcurrentDictionary<(Type ModelType, string FieldName), PropertyInfo?> _propertyInfoCache = new(); @@ -51,6 +62,11 @@ public DataAnnotationsEventSubscriptions(EditContext editContext) _editContext.OnFieldChanged += OnFieldChanged; _editContext.OnValidationRequested += OnValidationRequested; + + if (HotReloadFeature.IsSupported) + { + OnClearCache += ClearCache; + } } private void OnFieldChanged(object? sender, FieldChangedEventArgs eventArgs) @@ -115,6 +131,11 @@ public void Dispose() _editContext.OnFieldChanged -= OnFieldChanged; _editContext.OnValidationRequested -= OnValidationRequested; _editContext.NotifyValidationStateChanged(); + + if (HotReloadFeature.IsSupported) + { + OnClearCache -= ClearCache; + } } private static bool TryGetValidatableProperty(in FieldIdentifier fieldIdentifier, [NotNullWhen(true)] out PropertyInfo? propertyInfo) @@ -132,6 +153,11 @@ private static bool TryGetValidatableProperty(in FieldIdentifier fieldIdentifier return propertyInfo != null; } + + internal void ClearCache() + { + _propertyInfoCache.Clear(); + } } } } diff --git a/src/Components/Forms/src/Microsoft.AspNetCore.Components.Forms.csproj b/src/Components/Forms/src/Microsoft.AspNetCore.Components.Forms.csproj index 7ac8a9203e0f..12b071a02268 100644 --- a/src/Components/Forms/src/Microsoft.AspNetCore.Components.Forms.csproj +++ b/src/Components/Forms/src/Microsoft.AspNetCore.Components.Forms.csproj @@ -9,8 +9,19 @@ true + + + + + + + + + + ILLink.Substitutions.xml + diff --git a/src/Components/Forms/src/Properties/ILLink.Substitutions.xml b/src/Components/Forms/src/Properties/ILLink.Substitutions.xml new file mode 100644 index 000000000000..6e9602f33f95 --- /dev/null +++ b/src/Components/Forms/src/Properties/ILLink.Substitutions.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index ad96e5e9c97f..0bf276fb2956 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -6,11 +6,14 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Reflection.Metadata; using System.Runtime.ExceptionServices; using System.Text; using System.Text.Json; using System.Threading.Tasks; +[assembly: MetadataUpdateHandler(typeof(Microsoft.JSInterop.Infrastructure.DotNetDispatcher))] + namespace Microsoft.JSInterop.Infrastructure { /// @@ -426,6 +429,12 @@ private static Assembly GetRequiredLoadedAssembly(AssemblyKey assemblyKey) ?? throw new ArgumentException($"There is no loaded assembly with the name '{assemblyKey.AssemblyName}'."); } + private static void ClearCache(Type[]? _) + { + _cachedMethodsByAssembly.Clear(); + _cachedMethodsByType.Clear(); + } + private readonly struct AssemblyKey : IEquatable { public AssemblyKey(Assembly assembly) From 223cb4433a1a23327024ce0730811d375fdd92cf Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 11 May 2021 10:38:48 +0100 Subject: [PATCH 3/3] Rename variable for clarity --- src/Components/Components/src/Routing/Router.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Components/Components/src/Routing/Router.cs b/src/Components/Components/src/Routing/Router.cs index 91e89bbe86fe..e82af103b3da 100644 --- a/src/Components/Components/src/Routing/Router.cs +++ b/src/Components/Components/src/Routing/Router.cs @@ -34,7 +34,7 @@ static readonly IReadOnlyDictionary _emptyParametersDictionary private Task _previousOnNavigateTask = Task.CompletedTask; - private RouteKey _currentRouteKey; + private RouteKey _routeTableLastBuiltForRouteKey; private bool _onNavigateCalled = false; @@ -155,9 +155,9 @@ private void RefreshRouteTable() { var routeKey = new RouteKey(AppAssembly, AdditionalAssemblies); - if (!routeKey.Equals(_currentRouteKey)) + if (!routeKey.Equals(_routeTableLastBuiltForRouteKey)) { - _currentRouteKey = routeKey; + _routeTableLastBuiltForRouteKey = routeKey; Routes = RouteTableFactory.Create(routeKey); } } @@ -165,7 +165,7 @@ private void RefreshRouteTable() private void ClearRouteCaches() { RouteTableFactory.ClearCaches(); - _currentRouteKey = default; + _routeTableLastBuiltForRouteKey = default; } internal virtual void Refresh(bool isNavigationIntercepted)