@@ -18,11 +18,11 @@ namespace Microsoft.AspNetCore.Components.Infrastructure;
18
18
internal sealed class PersistentServicesRegistry
19
19
{
20
20
private static readonly string _registryKey = typeof ( PersistentServicesRegistry ) . FullName ! ;
21
- private static readonly RootTypeCache _persistentServiceTypeCache = new RootTypeCache ( ) ;
21
+ private static readonly RootTypeCache _persistentServiceTypeCache = new ( ) ;
22
22
23
23
private readonly IServiceProvider _serviceProvider ;
24
24
private IPersistentServiceRegistration [ ] _registrations ;
25
- private List < PersistingComponentStateSubscription > _subscriptions = [ ] ;
25
+ private List < ( PersistingComponentStateSubscription , RestoringComponentStateSubscription ) > _subscriptions = [ ] ;
26
26
private static readonly ConcurrentDictionary < Type , PropertiesAccessor > _cachedAccessorsByType = new ( ) ;
27
27
28
28
static PersistentServicesRegistry ( )
@@ -54,7 +54,9 @@ internal void RegisterForPersistence(PersistentComponentState state)
54
54
return ;
55
55
}
56
56
57
- var subscriptions = new List < PersistingComponentStateSubscription > ( _registrations . Length + 1 ) ;
57
+ UpdateRegistrations ( state ) ;
58
+ var subscriptions = new List < ( PersistingComponentStateSubscription , RestoringComponentStateSubscription ) > (
59
+ _registrations . Length + 1 ) ;
58
60
for ( var i = 0 ; i < _registrations . Length ; i ++ )
59
61
{
60
62
var registration = _registrations [ i ] ;
@@ -67,20 +69,29 @@ internal void RegisterForPersistence(PersistentComponentState state)
67
69
var renderMode = registration . GetRenderModeOrDefault ( ) ;
68
70
69
71
var instance = _serviceProvider . GetRequiredService ( type ) ;
70
- subscriptions . Add ( state . RegisterOnPersisting ( ( ) =>
71
- {
72
- PersistInstanceState ( instance , type , state ) ;
73
- return Task . CompletedTask ;
74
- } , renderMode ) ) ;
72
+ subscriptions . Add ( (
73
+ state . RegisterOnPersisting ( ( ) =>
74
+ {
75
+ PersistInstanceState ( instance , type , state ) ;
76
+ return Task . CompletedTask ;
77
+ } , renderMode ) ,
78
+ // In order to avoid registering one callback per property, we register a single callback with the most
79
+ // permissive options and perform the filtering inside of it.
80
+ state . RegisterOnRestoring ( ( ) =>
81
+ {
82
+ RestoreInstanceState ( instance , type , state ) ;
83
+ } , new RestoreOptions { AllowUpdates = true } ) ) ) ;
75
84
}
76
85
77
86
if ( RenderMode != null )
78
87
{
79
- subscriptions . Add ( state . RegisterOnPersisting ( ( ) =>
80
- {
81
- state . PersistAsJson ( _registryKey , _registrations ) ;
82
- return Task . CompletedTask ;
83
- } , RenderMode ) ) ;
88
+ subscriptions . Add ( (
89
+ state . RegisterOnPersisting ( ( ) =>
90
+ {
91
+ state . PersistAsJson ( _registryKey , _registrations ) ;
92
+ return Task . CompletedTask ;
93
+ } , RenderMode ) ,
94
+ default ) ) ;
84
95
}
85
96
86
97
_subscriptions = subscriptions ;
@@ -92,7 +103,7 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
92
103
var accessors = _cachedAccessorsByType . GetOrAdd ( instance . GetType ( ) , static ( runtimeType , declaredType ) => new PropertiesAccessor ( runtimeType , declaredType ) , type ) ;
93
104
foreach ( var ( key , propertyType ) in accessors . KeyTypePairs )
94
105
{
95
- var ( setter , getter ) = accessors . GetAccessor ( key ) ;
106
+ var ( setter , getter , options ) = accessors . GetAccessor ( key ) ;
96
107
var value = getter . GetValue ( instance ) ;
97
108
if ( value != null )
98
109
{
@@ -105,33 +116,12 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
105
116
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code" ,
106
117
Justification = "Types registered for persistence are preserved in the API call to register them and typically live in assemblies that aren't trimmed." ) ]
107
118
[ DynamicDependency ( LinkerFlags . JsonSerialized , typeof ( PersistentServiceRegistration ) ) ]
108
- internal void Restore ( PersistentComponentState state )
119
+ private void UpdateRegistrations ( PersistentComponentState state )
109
120
{
110
121
if ( state . TryTakeFromJson < PersistentServiceRegistration [ ] > ( _registryKey , out var registry ) && registry != null )
111
122
{
112
123
_registrations = ResolveRegistrations ( _registrations . Concat ( registry ) ) ;
113
124
}
114
-
115
- RestoreRegistrationsIfAvailable ( state ) ;
116
- }
117
-
118
- [ 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." ) ]
119
- private void RestoreRegistrationsIfAvailable ( PersistentComponentState state )
120
- {
121
- foreach ( var registration in _registrations )
122
- {
123
- var type = ResolveType ( registration ) ;
124
- if ( type == null )
125
- {
126
- continue ;
127
- }
128
-
129
- var instance = _serviceProvider . GetService ( type ) ;
130
- if ( instance != null )
131
- {
132
- RestoreInstanceState ( instance , type , state ) ;
133
- }
134
- }
135
125
}
136
126
137
127
[ RequiresUnreferencedCode ( "Calls Microsoft.AspNetCore.Components.PersistentComponentState.TryTakeFromJson(String, Type, out Object)" ) ]
@@ -140,9 +130,13 @@ private static void RestoreInstanceState(object instance, Type type, PersistentC
140
130
var accessors = _cachedAccessorsByType . GetOrAdd ( instance . GetType ( ) , static ( runtimeType , declaredType ) => new PropertiesAccessor ( runtimeType , declaredType ) , type ) ;
141
131
foreach ( var ( key , propertyType ) in accessors . KeyTypePairs )
142
132
{
133
+ var ( setter , getter , options ) = accessors . GetAccessor ( key ) ;
134
+ if ( ! state . CurrentContext . ShouldRestore ( options ) )
135
+ {
136
+ continue ;
137
+ }
143
138
if ( state . TryTakeFromJson ( key , propertyType , out var result ) )
144
139
{
145
- var ( setter , getter ) = accessors . GetAccessor ( key ) ;
146
140
setter . SetValue ( instance , result ! ) ;
147
141
}
148
142
}
@@ -165,12 +159,12 @@ private sealed class PropertiesAccessor
165
159
{
166
160
internal const BindingFlags BindablePropertyFlags = BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance | BindingFlags . IgnoreCase ;
167
161
168
- private readonly Dictionary < string , ( PropertySetter , PropertyGetter ) > _underlyingAccessors ;
162
+ private readonly Dictionary < string , ( PropertySetter , PropertyGetter , RestoreOptions ) > _underlyingAccessors ;
169
163
private readonly ( string , Type ) [ ] _cachedKeysForService ;
170
164
171
165
public PropertiesAccessor ( [ DynamicallyAccessedMembers ( LinkerFlags . Component ) ] Type targetType , Type keyType )
172
166
{
173
- _underlyingAccessors = new Dictionary < string , ( PropertySetter , PropertyGetter ) > ( StringComparer . OrdinalIgnoreCase ) ;
167
+ _underlyingAccessors = new Dictionary < string , ( PropertySetter , PropertyGetter , RestoreOptions ) > ( StringComparer . OrdinalIgnoreCase ) ;
174
168
175
169
var keys = new List < ( string , Type ) > ( ) ;
176
170
foreach ( var propertyInfo in GetCandidateBindableProperties ( targetType ) )
@@ -204,10 +198,16 @@ public PropertiesAccessor([DynamicallyAccessedMembers(LinkerFlags.Component)] Ty
204
198
$ "The type '{ targetType . FullName } ' declares a property matching the name '{ propertyName } ' that is not public. Persistent service properties must be public.") ;
205
199
}
206
200
201
+ var restoreOptions = new RestoreOptions
202
+ {
203
+ RestoreBehavior = parameterAttribute . RestoreBehavior ,
204
+ AllowUpdates = parameterAttribute . AllowUpdates ,
205
+ } ;
206
+
207
207
var propertySetter = new PropertySetter ( targetType , propertyInfo ) ;
208
208
var propertyGetter = new PropertyGetter ( targetType , propertyInfo ) ;
209
209
210
- _underlyingAccessors . Add ( key , ( propertySetter , propertyGetter ) ) ;
210
+ _underlyingAccessors . Add ( key , ( propertySetter , propertyGetter , restoreOptions ) ) ;
211
211
}
212
212
213
213
_cachedKeysForService = [ .. keys ] ;
@@ -236,7 +236,7 @@ internal static IEnumerable<PropertyInfo> GetCandidateBindableProperties(
236
236
[ DynamicallyAccessedMembers ( LinkerFlags . Component ) ] Type targetType )
237
237
=> MemberAssignment . GetPropertiesIncludingInherited ( targetType , BindablePropertyFlags ) ;
238
238
239
- internal ( PropertySetter setter , PropertyGetter getter ) GetAccessor ( string key ) =>
239
+ internal ( PropertySetter setter , PropertyGetter getter , RestoreOptions options ) GetAccessor ( string key ) =>
240
240
_underlyingAccessors . TryGetValue ( key , out var result ) ? result : default ;
241
241
}
242
242
0 commit comments