@@ -17,11 +17,11 @@ namespace Microsoft.AspNetCore.Components.Infrastructure;
17
17
internal sealed class PersistentServicesRegistry
18
18
{
19
19
private static readonly string _registryKey = typeof ( PersistentServicesRegistry ) . FullName ! ;
20
- private static readonly RootTypeCache _persistentServiceTypeCache = new RootTypeCache ( ) ;
20
+ private static readonly RootTypeCache _persistentServiceTypeCache = new ( ) ;
21
21
22
22
private readonly IServiceProvider _serviceProvider ;
23
23
private IPersistentServiceRegistration [ ] _registrations ;
24
- private List < PersistingComponentStateSubscription > _subscriptions = [ ] ;
24
+ private List < ( PersistingComponentStateSubscription , RestoringComponentStateSubscription ) > _subscriptions = [ ] ;
25
25
private static readonly ConcurrentDictionary < Type , PropertiesAccessor > _cachedAccessorsByType = new ( ) ;
26
26
27
27
public PersistentServicesRegistry ( IServiceProvider serviceProvider )
@@ -45,7 +45,9 @@ internal void RegisterForPersistence(PersistentComponentState state)
45
45
return ;
46
46
}
47
47
48
- var subscriptions = new List < PersistingComponentStateSubscription > ( _registrations . Length + 1 ) ;
48
+ UpdateRegistrations ( state ) ;
49
+ var subscriptions = new List < ( PersistingComponentStateSubscription , RestoringComponentStateSubscription ) > (
50
+ _registrations . Length + 1 ) ;
49
51
for ( var i = 0 ; i < _registrations . Length ; i ++ )
50
52
{
51
53
var registration = _registrations [ i ] ;
@@ -58,20 +60,29 @@ internal void RegisterForPersistence(PersistentComponentState state)
58
60
var renderMode = registration . GetRenderModeOrDefault ( ) ;
59
61
60
62
var instance = _serviceProvider . GetRequiredService ( type ) ;
61
- subscriptions . Add ( state . RegisterOnPersisting ( ( ) =>
62
- {
63
- PersistInstanceState ( instance , type , state ) ;
64
- return Task . CompletedTask ;
65
- } , renderMode ) ) ;
63
+ subscriptions . Add ( (
64
+ state . RegisterOnPersisting ( ( ) =>
65
+ {
66
+ PersistInstanceState ( instance , type , state ) ;
67
+ return Task . CompletedTask ;
68
+ } , renderMode ) ,
69
+ // In order to avoid registering one callback per property, we register a single callback with the most
70
+ // permissive options and perform the filtering inside of it.
71
+ state . RegisterOnRestoring ( ( ) =>
72
+ {
73
+ RestoreInstanceState ( instance , type , state ) ;
74
+ } , new RestoreOptions { AllowUpdates = true } ) ) ) ;
66
75
}
67
76
68
77
if ( RenderMode != null )
69
78
{
70
- subscriptions . Add ( state . RegisterOnPersisting ( ( ) =>
71
- {
72
- state . PersistAsJson ( _registryKey , _registrations ) ;
73
- return Task . CompletedTask ;
74
- } , RenderMode ) ) ;
79
+ subscriptions . Add ( (
80
+ state . RegisterOnPersisting ( ( ) =>
81
+ {
82
+ state . PersistAsJson ( _registryKey , _registrations ) ;
83
+ return Task . CompletedTask ;
84
+ } , RenderMode ) ,
85
+ default ) ) ;
75
86
}
76
87
77
88
_subscriptions = subscriptions ;
@@ -83,7 +94,7 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
83
94
var accessors = _cachedAccessorsByType . GetOrAdd ( instance . GetType ( ) , static ( runtimeType , declaredType ) => new PropertiesAccessor ( runtimeType , declaredType ) , type ) ;
84
95
foreach ( var ( key , propertyType ) in accessors . KeyTypePairs )
85
96
{
86
- var ( setter , getter ) = accessors . GetAccessor ( key ) ;
97
+ var ( setter , getter , options ) = accessors . GetAccessor ( key ) ;
87
98
var value = getter . GetValue ( instance ) ;
88
99
if ( value != null )
89
100
{
@@ -96,33 +107,12 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
96
107
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code" ,
97
108
Justification = "Types registered for persistence are preserved in the API call to register them and typically live in assemblies that aren't trimmed." ) ]
98
109
[ DynamicDependency ( LinkerFlags . JsonSerialized , typeof ( PersistentServiceRegistration ) ) ]
99
- internal void Restore ( PersistentComponentState state )
110
+ private void UpdateRegistrations ( PersistentComponentState state )
100
111
{
101
112
if ( state . TryTakeFromJson < PersistentServiceRegistration [ ] > ( _registryKey , out var registry ) && registry != null )
102
113
{
103
114
_registrations = ResolveRegistrations ( _registrations . Concat ( registry ) ) ;
104
115
}
105
-
106
- RestoreRegistrationsIfAvailable ( state ) ;
107
- }
108
-
109
- [ UnconditionalSuppressMessage ( "Trimming" , "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code" , Justification = "Types registered for persistence are preserved in the API call to register them and typically live in assemblies that aren't trimmed." ) ]
110
- private void RestoreRegistrationsIfAvailable ( PersistentComponentState state )
111
- {
112
- foreach ( var registration in _registrations )
113
- {
114
- var type = ResolveType ( registration ) ;
115
- if ( type == null )
116
- {
117
- continue ;
118
- }
119
-
120
- var instance = _serviceProvider . GetService ( type ) ;
121
- if ( instance != null )
122
- {
123
- RestoreInstanceState ( instance , type , state ) ;
124
- }
125
- }
126
116
}
127
117
128
118
[ RequiresUnreferencedCode ( "Calls Microsoft.AspNetCore.Components.PersistentComponentState.TryTakeFromJson(String, Type, out Object)" ) ]
@@ -131,9 +121,13 @@ private static void RestoreInstanceState(object instance, Type type, PersistentC
131
121
var accessors = _cachedAccessorsByType . GetOrAdd ( instance . GetType ( ) , static ( runtimeType , declaredType ) => new PropertiesAccessor ( runtimeType , declaredType ) , type ) ;
132
122
foreach ( var ( key , propertyType ) in accessors . KeyTypePairs )
133
123
{
124
+ var ( setter , getter , options ) = accessors . GetAccessor ( key ) ;
125
+ if ( ! state . CurrentContext . ShouldRestore ( options ) )
126
+ {
127
+ continue ;
128
+ }
134
129
if ( state . TryTakeFromJson ( key , propertyType , out var result ) )
135
130
{
136
- var ( setter , getter ) = accessors . GetAccessor ( key ) ;
137
131
setter . SetValue ( instance , result ! ) ;
138
132
}
139
133
}
@@ -156,12 +150,12 @@ private sealed class PropertiesAccessor
156
150
{
157
151
internal const BindingFlags BindablePropertyFlags = BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance | BindingFlags . IgnoreCase ;
158
152
159
- private readonly Dictionary < string , ( PropertySetter , PropertyGetter ) > _underlyingAccessors ;
153
+ private readonly Dictionary < string , ( PropertySetter , PropertyGetter , RestoreOptions ) > _underlyingAccessors ;
160
154
private readonly ( string , Type ) [ ] _cachedKeysForService ;
161
155
162
156
public PropertiesAccessor ( [ DynamicallyAccessedMembers ( LinkerFlags . Component ) ] Type targetType , Type keyType )
163
157
{
164
- _underlyingAccessors = new Dictionary < string , ( PropertySetter , PropertyGetter ) > ( StringComparer . OrdinalIgnoreCase ) ;
158
+ _underlyingAccessors = new Dictionary < string , ( PropertySetter , PropertyGetter , RestoreOptions ) > ( StringComparer . OrdinalIgnoreCase ) ;
165
159
166
160
var keys = new List < ( string , Type ) > ( ) ;
167
161
foreach ( var propertyInfo in GetCandidateBindableProperties ( targetType ) )
@@ -195,10 +189,16 @@ public PropertiesAccessor([DynamicallyAccessedMembers(LinkerFlags.Component)] Ty
195
189
$ "The type '{ targetType . FullName } ' declares a property matching the name '{ propertyName } ' that is not public. Persistent service properties must be public.") ;
196
190
}
197
191
192
+ var restoreOptions = new RestoreOptions
193
+ {
194
+ RestoreBehavior = parameterAttribute . RestoreBehavior ,
195
+ AllowUpdates = parameterAttribute . AllowUpdates ,
196
+ } ;
197
+
198
198
var propertySetter = new PropertySetter ( targetType , propertyInfo ) ;
199
199
var propertyGetter = new PropertyGetter ( targetType , propertyInfo ) ;
200
200
201
- _underlyingAccessors . Add ( key , ( propertySetter , propertyGetter ) ) ;
201
+ _underlyingAccessors . Add ( key , ( propertySetter , propertyGetter , restoreOptions ) ) ;
202
202
}
203
203
204
204
_cachedKeysForService = [ .. keys ] ;
@@ -227,7 +227,7 @@ internal static IEnumerable<PropertyInfo> GetCandidateBindableProperties(
227
227
[ DynamicallyAccessedMembers ( LinkerFlags . Component ) ] Type targetType )
228
228
=> MemberAssignment . GetPropertiesIncludingInherited ( targetType , BindablePropertyFlags ) ;
229
229
230
- internal ( PropertySetter setter , PropertyGetter getter ) GetAccessor ( string key ) =>
230
+ internal ( PropertySetter setter , PropertyGetter getter , RestoreOptions options ) GetAccessor ( string key ) =>
231
231
_underlyingAccessors . TryGetValue ( key , out var result ) ? result : default ;
232
232
}
233
233
0 commit comments