Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal static class AttributeAuthorizeDataCache
{
static AttributeAuthorizeDataCache()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += ClearCache;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@
<Compile Include="$(ComponentsSharedSourceRoot)src\HotReloadManager.cs" LinkBase="HotReload" />
</ItemGroup>

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

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<linker>
<assembly fullname="Microsoft.AspNetCore.Components.Authorization">
<type fullname="Microsoft.AspNetCore.Components.HotReload.HotReloadManager" feature="System.Reflection.Metadata.MetadataUpdater.IsSupported" featurevalue="false">
<method signature="System.Boolean get_IsSupported()" body="stub" value="false" />
</type>
</assembly>
</linker>
4 changes: 2 additions & 2 deletions src/Components/Components/src/BindConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1670,7 +1670,7 @@ private static class FormatterDelegateCache

static FormatterDelegateCache()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += _cache.Clear;
}
Expand Down Expand Up @@ -1867,7 +1867,7 @@ internal static class ParserDelegateCache

static ParserDelegateCache()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += _cache.Clear;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Components/src/ChangeDetection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal sealed class ChangeDetection

static ChangeDetection()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += _immutableObjectTypesCache.Clear;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Components/src/ComponentFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal sealed class ComponentFactory

static ComponentFactory()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += ClearCache;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal sealed class DefaultComponentActivator(IServiceProvider serviceProvider

static DefaultComponentActivator()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += ClearCache;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ private const BindingFlags InjectablePropertyBindingFlags

static DefaultComponentPropertyActivator()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += ClearCache;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal sealed class PersistentServicesRegistry

static PersistentServicesRegistry()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += _cachedAccessorsByType.Clear;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal static class PersistentStateValueProviderKeyResolver

static PersistentStateValueProviderKeyResolver()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += ClearCaches;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal partial class PersistentValueProviderComponentSubscription : IDisposabl

static PersistentValueProviderComponentSubscription()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += ClearCaches;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<linker>
<assembly fullname="Microsoft.AspNetCore.Components">
<type fullname="Microsoft.AspNetCore.Components.HotReload.HotReloadManager" feature="System.Reflection.Metadata.MetadataUpdater.IsSupported" featurevalue="false">
<method signature="System.Boolean get_MetadataUpdateSupported()" body="stub" value="false" />
<method signature="System.Boolean get_IsSupported()" body="stub" value="false" />
</type>
<type fullname="Microsoft.AspNetCore.Components.ComponentsMetrics" feature="System.Diagnostics.Metrics.Meter.IsSupported" featurevalue="false">
<method signature="System.Boolean get_IsSupported()" body="stub" value="false" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal static class ComponentProperties
{
static ComponentProperties()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += ClearCache;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Components/src/RenderHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public Dispatcher Dispatcher
/// <summary>
/// Gets a value that determines if the <see cref="Renderer"/> is triggering a render in response to a metadata update (hot-reload) change.
/// </summary>
public bool IsRenderingOnMetadataUpdate => HotReloadManager.Default.MetadataUpdateSupported && (_renderer?.IsRenderingOnMetadataUpdate ?? false);
public bool IsRenderingOnMetadataUpdate => HotReloadManager.IsSupported && (_renderer?.IsRenderingOnMetadataUpdate ?? false);

internal bool IsRendererDisposed => _renderer?.Disposed
?? throw new InvalidOperationException("No renderer has been initialized.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal static class EventArgsTypeCache

static EventArgsTypeCache()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += Cache.Clear;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ private static void AppendDiffEntriesForFramesWithSameSequence(
var oldParameters = new ParameterView(ParameterViewLifetime.Unbound, oldTree, oldFrameIndex);
var newParametersLifetime = new ParameterViewLifetime(diffContext.BatchBuilder);
var newParameters = new ParameterView(newParametersLifetime, newTree, newFrameIndex);
var isHotReload = HotReloadManager.Default.MetadataUpdateSupported && diffContext.Renderer.IsRenderingOnMetadataUpdate;
var isHotReload = HotReloadManager.IsSupported && diffContext.Renderer.IsRenderingOnMetadataUpdate;

if (isHotReload && newParameters.HasRemovedDirectParameters(oldParameters))
{
Expand Down Expand Up @@ -754,13 +754,13 @@ private static string CreateDiffErrorMessage(ref DiffContext diffContext, int ne
{
var newTree = diffContext.NewTree;
var unsupportedFrameType = newTree[newFrameIndex].FrameTypeField;

// Build component hierarchy path
var componentPath = BuildComponentPath(diffContext.Renderer, diffContext.ComponentId);

// Build frame types descriptor
var frameTypesDescriptor = BuildFrameTypeDescriptor(newTree, newFrameIndex);

return $"Encountered an unsupported frame type during diffing {unsupportedFrameType} for Component Path: '{componentPath}' on tree with length '{newTree.Length}' and contents '{frameTypesDescriptor}'.";
}

Expand All @@ -771,22 +771,22 @@ private static string BuildFrameTypeDescriptor(RenderTreeFrame[] renderTree, int
{
frameTypes.Add(renderTree[i].FrameTypeField.ToString());
}

return string.Join(", ", frameTypes);
}

private static string BuildComponentPath(Renderer renderer, int componentId)
{
var componentPath = new List<string>();
var currentComponentState = renderer.GetRequiredComponentState(componentId);

while (currentComponentState is not null)
{
var componentType = currentComponentState.Component.GetType();
componentPath.Insert(0, componentType.Name);
currentComponentState = currentComponentState.ParentComponentState;
}

return string.Join(" -> ", componentPath);
}

Expand Down
10 changes: 5 additions & 5 deletions src/Components/Components/src/RenderTree/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory,

internal ICascadingValueSupplier[] ServiceProviderCascadingValueSuppliers { get; }

internal HotReloadManager HotReloadManager { get; set; } = HotReloadManager.Default;
internal HotReloadManager? HotReloadManager { get; set; } = HotReloadManager.IsSupported ? HotReloadManager.Default : null;
Comment thread
pavelsavara marked this conversation as resolved.

private static IComponentActivator GetComponentActivatorOrDefault(IServiceProvider serviceProvider)
{
Expand Down Expand Up @@ -243,7 +243,7 @@ protected internal int AssignRootComponentId(IComponent component)
if (!_hotReloadInitialized)
{
_hotReloadInitialized = true;
if (HotReloadManager.MetadataUpdateSupported)
if (HotReload.HotReloadManager.IsSupported && HotReloadManager != null)
{
// Capture the current ExecutionContext so AsyncLocal values present during initial root component
// registration flow through to hot reload re-renders. Without this, hot reload callbacks execute
Expand Down Expand Up @@ -308,7 +308,7 @@ protected internal async Task RenderRootComponentAsync(int componentId, Paramete
_pendingTasks ??= new();

var componentState = GetRequiredRootComponentState(componentId);
if (HotReloadManager.MetadataUpdateSupported)
if (HotReload.HotReloadManager.IsSupported && HotReloadManager != null)
{
// 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.
Expand Down Expand Up @@ -338,7 +338,7 @@ protected internal void RemoveRootComponent(int componentId)
// Currently there's no known scenario where we need to support calling RemoveRootComponentAsync
// during a batch, but if a scenario emerges we can add support.
_batchBuilder.ComponentDisposalQueue.Enqueue(componentId);
if (HotReloadManager.MetadataUpdateSupported)
if (HotReload.HotReloadManager.IsSupported && HotReloadManager != null)
{
Comment thread
pavelsavara marked this conversation as resolved.
_rootComponentsLatestParameters?.Remove(componentId);
}
Expand Down Expand Up @@ -1253,7 +1253,7 @@ protected virtual void Dispose(bool disposing)
_rendererIsDisposed = true;
}

if (_hotReloadInitialized && HotReloadManager.MetadataUpdateSupported && _hotReloadRenderHandler is not null)
if (HotReload.HotReloadManager.IsSupported && HotReloadManager != null && _hotReloadInitialized && _hotReloadRenderHandler is not null)
{
HotReloadManager.OnDeltaApplied -= _hotReloadRenderHandler.RerenderOnHotReload;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Components/src/RouteView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class RouteView : IComponent

static RouteView()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += _layoutAttributeCache.Clear;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Components/src/Routing/RouteTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal sealed class RouteTable(TreeRouter treeRouter)

static RouteTable()
{
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += _routeEntryCache.Clear;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Components/Components/src/Routing/Router.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public void Attach(RenderHandle renderHandle)
NavigationManager.OnNotFound += OnNotFound;
RoutingStateProvider = ServiceProvider.GetService<IRoutingStateProvider>();

if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied += ClearRouteCaches;
}
Expand Down Expand Up @@ -179,7 +179,7 @@ public void Dispose()
{
NavigationManager.LocationChanged -= OnLocationChanged;
NavigationManager.OnNotFound -= OnNotFound;
if (HotReloadManager.Default.MetadataUpdateSupported)
if (HotReloadManager.IsSupported)
{
HotReloadManager.Default.OnDeltaApplied -= ClearRouteCaches;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<RootNamespace>Microsoft.AspNetCore.Components</RootNamespace>
<!-- Tests need hot reload support enabled even in Release to test HotReloadManager behavior -->
<MetadataUpdaterSupport>true</MetadataUpdaterSupport>
</PropertyGroup>

<ItemGroup>
Expand Down
52 changes: 31 additions & 21 deletions src/Components/Components/test/RendererTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4982,33 +4982,42 @@ public async Task DisposeAsyncCallsComponentDisposeAsyncOnSyncContext()
}

[Fact]
public async Task NoHotReloadListenersAreRegistered_WhenMetadataUpdatesAreNotSupported()
public async Task NoHotReloadListenersAreRegistered_WhenHotReloadIsDisabled()
{
// Arrange
await using var renderer = new TestRenderer();
var hotReloadManager = new HotReloadManager { MetadataUpdateSupported = false };
renderer.HotReloadManager = hotReloadManager;
var component = new TestComponent(builder =>
try
{
builder.OpenElement(0, "h2");
builder.AddContent(1, "some text");
builder.CloseElement();
});
AppContext.SetSwitch("System.Reflection.Metadata.MetadataUpdater.IsSupported", false);
await using var renderer = new TestRenderer();
var hotReloadManager = new HotReloadManager();
renderer.HotReloadManager = hotReloadManager;
var component = new TestComponent(builder =>
{
builder.OpenElement(0, "h2");
builder.AddContent(1, "some text");
builder.CloseElement();
});

// Act
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
Assert.False(hotReloadManager.IsSubscribedTo);
// Act
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
Assert.False(hotReloadManager.IsSubscribedTo);

await renderer.DisposeAsync();
await renderer.DisposeAsync();
}
finally
{
AppContext.SetSwitch("System.Reflection.Metadata.MetadataUpdater.IsSupported", true);
}
}

[Fact]
public async Task DisposingRenderer_UnsubsribesFromHotReloadManager()
{
// Arrange
AppContext.SetSwitch("System.Reflection.Metadata.MetadataUpdater.IsSupported", true);
var renderer = new TestRenderer();
var hotReloadManager = new HotReloadManager { MetadataUpdateSupported = true };
var hotReloadManager = new HotReloadManager();
renderer.HotReloadManager = hotReloadManager;
var component = new TestComponent(builder =>
{
Expand All @@ -5031,11 +5040,12 @@ public async Task DisposingRenderer_UnsubsribesFromHotReloadManager()
[Fact]
public async Task HotReload_ReRenderPreservesAsyncLocalValues()
{
AppContext.SetSwitch("System.Reflection.Metadata.MetadataUpdater.IsSupported", true);

await using var renderer = new TestRenderer();

var hotReloadManager = new HotReloadManager { MetadataUpdateSupported = true };
var hotReloadManager = new HotReloadManager();
renderer.HotReloadManager = hotReloadManager;
HotReloadManager.Default.MetadataUpdateSupported = true;

var component = new AsyncLocalCaptureComponent();

Expand Down Expand Up @@ -5223,7 +5233,7 @@ public void RenderFragmentContravariance_WorksWithInterfaceHierarchy()
{
// C# variance only works with reference types. This test uses interface hierarchy.
// IComparable<T> is contravariant, so we can demonstrate the concept

// Arrange - Create a fragment that accepts any IComparable
RenderFragment<IComparable> baseFragment = (IComparable value) => builder =>
{
Expand All @@ -5244,7 +5254,7 @@ public void RenderFragmentContravariance_WorksWithInterfaceHierarchy()
public void RenderFragmentContravariance_WorksWithObjectToPrimitiveWrapper()
{
// For value types, contravariance only works when going through object (boxing)

// Arrange - Create a fragment that accepts object
RenderFragment<object> baseFragment = (object value) => builder =>
{
Expand All @@ -5265,7 +5275,7 @@ public void RenderFragmentContravariance_WorksWithObjectToPrimitiveWrapper()
public void RenderFragmentContravariance_WorksWithComparableTypes()
{
// Demonstrating contravariance with reference types implementing IComparable

// Arrange - Create a fragment that accepts IComparable
RenderFragment<IComparable> baseFragment = (IComparable value) => builder =>
{
Expand Down Expand Up @@ -6332,7 +6342,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
private struct TestStructWithInterface : IComparable
{
public int Value { get; set; }

public int CompareTo(object obj)
{
if (obj is TestStructWithInterface other)
Expand Down
Loading
Loading