Skip to content

Commit

Permalink
Clear caches from more places on a hot reload event (#32509)
Browse files Browse the repository at this point in the history
* Use feature switch to perform hot reload trimming

Contributes to dotnet/runtime#51159

* Clear caches from more places on a hot reload event

* Rename variable for clarity

Co-authored-by: Steve Sanderson <SteveSandersonMS@users.noreply.github.com>
  • Loading branch information
pranavkm and SteveSandersonMS authored May 12, 2021
1 parent a7a6cdd commit a14d768
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 5 deletions.
4 changes: 3 additions & 1 deletion src/Components/Components/src/ComponentFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<argument>ILLink</argument>
<argument>IL2026</argument>
<property name="Scope">member</property>
<property name="Target">M:Microsoft.AspNetCore.Components.RouteTableFactory.&lt;GetRouteableComponents&gt;g__GetRouteableComponents|3_0(System.Collections.Generic.List{System.Type},System.Reflection.Assembly)</property>
<property name="Target">M:Microsoft.AspNetCore.Components.RouteTableFactory.&lt;GetRouteableComponents&gt;g__GetRouteableComponents|4_0(System.Collections.Generic.List{System.Type},System.Reflection.Assembly)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ internal static class ComponentProperties
private readonly static ConcurrentDictionary<Type, WritersForType> _cachedWritersByType
= new ConcurrentDictionary<Type, WritersForType>();

public static void ClearCache() => _cachedWritersByType.Clear();

public static void SetProperties(in ParameterView parameters, object target)
{
if (target == null)
Expand Down
5 changes: 5 additions & 0 deletions src/Components/Components/src/RenderTree/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/Components/Components/src/Routing/RouteTableFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public static RouteTable Create(RouteKey routeKey)
return routeTable;
}

public static void ClearCaches() => Cache.Clear();

private static List<Type> GetRouteableComponents(RouteKey routeKey)
{
var routeableComponents = new List<Type>();
Expand Down
22 changes: 19 additions & 3 deletions src/Components/Components/src/Routing/Router.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -33,7 +34,7 @@ static readonly IReadOnlyDictionary<string, object> _emptyParametersDictionary

private Task _previousOnNavigateTask = Task.CompletedTask;

private RouteKey _currentRouteKey;
private RouteKey _routeTableLastBuiltForRouteKey;

private bool _onNavigateCalled = false;

Expand Down Expand Up @@ -91,6 +92,11 @@ public void Attach(RenderHandle renderHandle)
_baseUri = NavigationManager.BaseUri;
_locationAbsolute = NavigationManager.Uri;
NavigationManager.LocationChanged += OnLocationChanged;

if (HotReloadFeature.IsSupported)
{
HotReloadManager.OnDeltaApplied += ClearRouteCaches;
}
}

/// <inheritdoc />
Expand Down Expand Up @@ -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)
Expand All @@ -145,13 +155,19 @@ 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);
}
}

private void ClearRouteCaches()
{
RouteTableFactory.ClearCaches();
_routeTableLastBuiltForRouteKey = default;
}

internal virtual void Refresh(bool isNavigationIntercepted)
{
// If an `OnNavigateAsync` task is currently in progress, then wait
Expand Down
26 changes: 26 additions & 0 deletions src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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();
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -132,6 +153,11 @@ private static bool TryGetValidatableProperty(in FieldIdentifier fieldIdentifier

return propertyInfo != null;
}

internal void ClearCache()
{
_propertyInfoCache.Clear();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,19 @@
<Trimmable>true</Trimmable>
</PropertyGroup>

<ItemGroup>
<None Remove="Properties\ILLink.Substitutions.xml" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Components" />
<Compile Include="$(ComponentsSharedSourceRoot)src\HotReloadFeature.cs" LinkBase="HotReload" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Properties\ILLink.Substitutions.xml">
<LogicalName>ILLink.Substitutions.xml</LogicalName>
</EmbeddedResource>
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions src/Components/Forms/src/Properties/ILLink.Substitutions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<linker>
<assembly fullname="Microsoft.AspNetCore.Components.Forms">
<type fullname="Microsoft.AspNetCore.Components.HotReload.HotReloadFeature" feature="System.Diagnostics.Debugger.IsSupported" featurevalue="false">
<method signature="System.Boolean get_IsSupported()" body="stub" value="false" />
</type>
</assembly>
</linker>
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
Expand Down Expand Up @@ -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<AssemblyKey>
{
public AssemblyKey(Assembly assembly)
Expand Down

0 comments on commit a14d768

Please sign in to comment.