Skip to content

Commit 46b97eb

Browse files
committed
Cleanups
1 parent e36abd6 commit 46b97eb

8 files changed

+100
-140
lines changed

src/Components/Components/src/PersistentComponentState.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ public PersistingComponentStateSubscription RegisterOnPersisting(Func<Task> call
5656
{
5757
ArgumentNullException.ThrowIfNull(callback);
5858

59+
if (PersistingState)
60+
{
61+
throw new InvalidOperationException("Registering a callback during while persisting state is not allowed.");
62+
}
63+
5964
var persistenceCallback = new PersistComponentStateRegistration(callback, renderMode);
6065

6166
_registeredCallbacks.Add(persistenceCallback);

src/Components/Components/src/PersistentState/ComponentStatePersistenceManager.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,6 @@ private void InferRenderModes(Renderer renderer)
152152
continue;
153153
}
154154

155-
if (registration.Callback.Target is PersistentServicesRegistry)
156-
{
157-
// The registration callback is associated with the services registry, which is a special case.
158-
// We don't need to infer the render mode for this case.
159-
continue;
160-
}
161-
162155
throw new InvalidOperationException(
163156
$"The registered callback {registration.Callback.Method.Name} must be associated with a component or define" +
164157
$" an explicit render mode type during registration.");
@@ -169,6 +162,12 @@ internal Task PauseAsync(IPersistentComponentStateStore store)
169162
{
170163
List<Task>? pendingCallbackTasks = null;
171164

165+
// We are iterating backwards to allow the callbacks to remove themselves from the list.
166+
// Otherwise, we would have to make a copy of the list to avoid running into situations
167+
// where we don't run all the callbacks because the count of the list changed while we
168+
// were iterating over it.
169+
// It is not allowed to register a callback while we are persisting the state, so we don't
170+
// need to worry about new callbacks being added to the list.
172171
for (var i = _registeredCallbacks.Count - 1; i >= 0; i--)
173172
{
174173
var registration = _registeredCallbacks[i];

src/Components/Components/src/PersistentState/IPersistentComponentRegistration.cs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,11 @@
33

44
namespace Microsoft.AspNetCore.Components;
55

6-
internal interface IPersistentComponentRegistration : IComparable<IPersistentComponentRegistration>, IEquatable<IPersistentComponentRegistration>
6+
// Represents a component that is registered for state persistence.
7+
internal interface IPersistentComponentRegistration
78
{
89
public string Assembly { get; }
910
public string FullTypeName { get; }
1011

1112
public IComponentRenderMode? GetRenderModeOrDefault();
12-
13-
int IComparable<IPersistentComponentRegistration>.CompareTo(IPersistentComponentRegistration? other)
14-
{
15-
var assemblyComparison = string.Compare(Assembly, other?.Assembly, StringComparison.Ordinal);
16-
if (assemblyComparison != 0)
17-
{
18-
return assemblyComparison;
19-
}
20-
return string.Compare(FullTypeName, other?.FullTypeName, StringComparison.Ordinal);
21-
}
22-
23-
bool IEquatable<IPersistentComponentRegistration>.Equals(IPersistentComponentRegistration? other)
24-
{
25-
return string.Equals(Assembly, other?.Assembly, StringComparison.Ordinal) &&
26-
string.Equals(FullTypeName, other?.FullTypeName, StringComparison.Ordinal);
27-
}
2813
}

src/Components/Components/src/PersistentState/PersistentComponentRegistration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace Microsoft.AspNetCore.Components;
77

88
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
9-
internal class PersistentComponentRegistration<TService>(IComponentRenderMode componentRenderMode) : IPersistentComponentRegistration
9+
internal sealed class PersistentComponentRegistration<TService>(IComponentRenderMode componentRenderMode) : IPersistentComponentRegistration
1010
{
1111
public string Assembly => typeof(TService).Assembly.GetName().Name!;
1212
public string FullTypeName => typeof(TService).FullName!;

src/Components/Components/src/PersistentState/PersistentServiceRenderMode.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/Components/Components/src/PersistentState/PersistentServiceTypeCache.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Microsoft.AspNetCore.Components;
99

10-
// A cache for registered persistent services. This is similar to the `RootComponentTypeCache`.
10+
// A cache for registered persistent services. This is similar to the RootComponentTypeCache.
1111
internal sealed class PersistentServiceTypeCache
1212
{
1313
private readonly ConcurrentDictionary<Key, Type?> _typeToKeyLookUp = new();

src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs

Lines changed: 53 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
namespace Microsoft.AspNetCore.Components.Infrastructure;
1616

17-
internal class PersistentServicesRegistry
17+
internal sealed class PersistentServicesRegistry
1818
{
1919
private static readonly string _registryKey = typeof(PersistentServicesRegistry).FullName!;
2020

@@ -23,60 +23,54 @@ internal class PersistentServicesRegistry
2323
private IPersistentComponentRegistration[] _registrations;
2424
private List<PersistingComponentStateSubscription> _subscriptions = [];
2525
private static readonly ConcurrentDictionary<Type, PropertiesAccessor> _cachedAccessorsByType = new();
26-
2726
public IComponentRenderMode? RenderMode { get; internal set; }
2827

2928
public PersistentServicesRegistry(IServiceProvider serviceProvider)
3029
{
3130
var registrations = serviceProvider.GetRequiredService<IEnumerable<IPersistentComponentRegistration>>();
3231
_serviceProvider = serviceProvider;
3332
_persistentServiceTypeCache = new PersistentServiceTypeCache();
34-
_registrations = [.. registrations.Distinct().Order()];
35-
}
36-
37-
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
38-
private class PersistentComponentRegistration : IPersistentComponentRegistration
39-
{
40-
public string Assembly { get; set; } = "";
41-
42-
public string FullTypeName { get; set; } = "";
43-
44-
public IComponentRenderMode? GetRenderModeOrDefault() => null;
45-
46-
private string GetDebuggerDisplay() => $"{Assembly}::{FullTypeName}";
33+
_registrations = [.. registrations.DistinctBy(r => (r.Assembly, r.FullTypeName)).OrderBy(r => r.Assembly).ThenBy(r => r.FullTypeName)];
4734
}
4835

4936
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
50-
private void RestoreRegistrationsIfAvailable(PersistentComponentState state)
37+
internal void RegisterForPersistence(PersistentComponentState state)
5138
{
52-
foreach (var registration in _registrations)
39+
if (_subscriptions.Count != 0)
5340
{
41+
return;
42+
}
43+
44+
var subscriptions = new List<PersistingComponentStateSubscription>(_registrations.Length + 1);
45+
for (var i = 0; i < _registrations.Length; i++)
46+
{
47+
var registration = _registrations[i];
5448
var type = ResolveType(registration.Assembly, registration.FullTypeName);
5549
if (type == null)
5650
{
5751
continue;
5852
}
5953

60-
var instance = _serviceProvider.GetService(type);
61-
if (instance != null)
54+
var renderMode = registration.GetRenderModeOrDefault();
55+
56+
var instance = _serviceProvider.GetRequiredService(type);
57+
subscriptions.Add(state.RegisterOnPersisting(() =>
6258
{
63-
RestoreInstanceState(instance, type, state);
64-
}
59+
PersistInstanceState(instance, type, state);
60+
return Task.CompletedTask;
61+
}, renderMode));
6562
}
66-
}
6763

68-
[RequiresUnreferencedCode("Calls Microsoft.AspNetCore.Components.PersistentComponentState.TryTakeFromJson(String, Type, out Object)")]
69-
private static void RestoreInstanceState(object instance, Type type, PersistentComponentState state)
70-
{
71-
var accessors = _cachedAccessorsByType.GetOrAdd(instance.GetType(), static (runtimeType, declaredType) => new PropertiesAccessor(runtimeType, declaredType), type);
72-
foreach (var (key, propertyType) in accessors.KeyTypePairs)
64+
if(RenderMode != null)
7365
{
74-
if (state.TryTakeFromJson(key, propertyType, out var result))
66+
subscriptions.Add(state.RegisterOnPersisting(() =>
7567
{
76-
var (setter, getter) = accessors.GetAccessor(key);
77-
setter.SetValue(instance, result!);
78-
}
68+
state.PersistAsJson(_registryKey, _registrations);
69+
return Task.CompletedTask;
70+
}, RenderMode));
7971
}
72+
73+
_subscriptions = subscriptions;
8074
}
8175

8276
[RequiresUnreferencedCode("Calls Microsoft.AspNetCore.Components.PersistentComponentState.PersistAsJson(String, Object, Type)")]
@@ -94,8 +88,6 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
9488
}
9589
}
9690

97-
private Type? ResolveType(string assembly, string fullTypeName) => _persistentServiceTypeCache.GetPersistentService(assembly, fullTypeName);
98-
9991
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
10092
internal void Restore(PersistentComponentState state)
10193
{
@@ -110,45 +102,41 @@ internal void Restore(PersistentComponentState state)
110102
}
111103

112104
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
113-
internal void RegisterForPersistence(PersistentComponentState state)
105+
private void RestoreRegistrationsIfAvailable(PersistentComponentState state)
114106
{
115-
if (_subscriptions.Count != 0)
116-
{
117-
return;
118-
}
119-
120-
var subscriptions = new List<PersistingComponentStateSubscription>(_registrations.Length + 1);
121-
for (var i = 0; i < _registrations.Length; i++)
107+
foreach (var registration in _registrations)
122108
{
123-
var registration = _registrations[i];
124109
var type = ResolveType(registration.Assembly, registration.FullTypeName);
125110
if (type == null)
126111
{
127112
continue;
128113
}
129114

130-
var renderMode = registration.GetRenderModeOrDefault();
131-
132-
var instance = _serviceProvider.GetRequiredService(type);
133-
subscriptions.Add(state.RegisterOnPersisting(() =>
115+
var instance = _serviceProvider.GetService(type);
116+
if (instance != null)
134117
{
135-
PersistInstanceState(instance, type, state);
136-
return Task.CompletedTask;
137-
}, renderMode));
118+
RestoreInstanceState(instance, type, state);
119+
}
138120
}
121+
}
139122

140-
if(RenderMode != null)
123+
[RequiresUnreferencedCode("Calls Microsoft.AspNetCore.Components.PersistentComponentState.TryTakeFromJson(String, Type, out Object)")]
124+
private static void RestoreInstanceState(object instance, Type type, PersistentComponentState state)
125+
{
126+
var accessors = _cachedAccessorsByType.GetOrAdd(instance.GetType(), static (runtimeType, declaredType) => new PropertiesAccessor(runtimeType, declaredType), type);
127+
foreach (var (key, propertyType) in accessors.KeyTypePairs)
141128
{
142-
subscriptions.Add(state.RegisterOnPersisting(() =>
129+
if (state.TryTakeFromJson(key, propertyType, out var result))
143130
{
144-
state.PersistAsJson(_registryKey, _registrations);
145-
return Task.CompletedTask;
146-
}, RenderMode));
131+
var (setter, getter) = accessors.GetAccessor(key);
132+
setter.SetValue(instance, result!);
133+
}
147134
}
148-
149-
_subscriptions = subscriptions;
150135
}
151136

137+
138+
private Type? ResolveType(string assembly, string fullTypeName) => _persistentServiceTypeCache.GetPersistentService(assembly, fullTypeName);
139+
152140
private sealed class PropertiesAccessor
153141
{
154142
internal const BindingFlags BindablePropertyFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase;
@@ -216,21 +204,19 @@ internal static IEnumerable<PropertyInfo> GetCandidateBindableProperties(
216204
[DynamicallyAccessedMembers(LinkerFlags.Component)] Type targetType)
217205
=> MemberAssignment.GetPropertiesIncludingInherited(targetType, BindablePropertyFlags);
218206

219-
internal (PropertySetter setter, PropertyGetter getter) GetAccessor(string key)
220-
{
221-
return _underlyingAccessors.TryGetValue(key, out var result) ? result : default;
222-
}
207+
internal (PropertySetter setter, PropertyGetter getter) GetAccessor(string key) =>
208+
_underlyingAccessors.TryGetValue(key, out var result) ? result : default;
223209
}
224210

225-
private class RenderModeComparer : IEqualityComparer<IComponentRenderMode?>
211+
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
212+
private class PersistentComponentRegistration : IPersistentComponentRegistration
226213
{
227-
public static RenderModeComparer Instance { get; } = new RenderModeComparer();
214+
public string Assembly { get; set; } = "";
228215

229-
public bool Equals(IComponentRenderMode? x, IComponentRenderMode? y)
230-
{
231-
return x?.GetType() == y?.GetType();
232-
}
216+
public string FullTypeName { get; set; } = "";
233217

234-
public int GetHashCode([DisallowNull] IComponentRenderMode? obj) => obj?.GetHashCode() ?? 1;
218+
public IComponentRenderMode? GetRenderModeOrDefault() => null;
219+
220+
private string GetDebuggerDisplay() => $"{Assembly}::{FullTypeName}";
235221
}
236222
}

0 commit comments

Comments
 (0)