Skip to content
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

Clear caches from more places on a hot reload event #32509

Merged
merged 4 commits into from
May 12, 2021
Merged
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
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
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>
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();
}
SteveSandersonMS marked this conversation as resolved.
Show resolved Hide resolved

private readonly struct AssemblyKey : IEquatable<AssemblyKey>
{
public AssemblyKey(Assembly assembly)
Expand Down