@@ -18,11 +18,11 @@ namespace Microsoft.AspNetCore.Components.Infrastructure;
1818internal sealed class PersistentServicesRegistry
1919{
2020 private static readonly string _registryKey = typeof ( PersistentServicesRegistry ) . FullName ! ;
21- private static readonly RootTypeCache _persistentServiceTypeCache = new RootTypeCache ( ) ;
21+ private static readonly RootTypeCache _persistentServiceTypeCache = new ( ) ;
2222
2323 private readonly IServiceProvider _serviceProvider ;
2424 private IPersistentServiceRegistration [ ] _registrations ;
25- private List < PersistingComponentStateSubscription > _subscriptions = [ ] ;
25+ private List < ( PersistingComponentStateSubscription , RestoringComponentStateSubscription ) > _subscriptions = [ ] ;
2626 private static readonly ConcurrentDictionary < Type , PropertiesAccessor > _cachedAccessorsByType = new ( ) ;
2727
2828 static PersistentServicesRegistry ( )
@@ -54,7 +54,9 @@ internal void RegisterForPersistence(PersistentComponentState state)
5454 return ;
5555 }
5656
57- var subscriptions = new List < PersistingComponentStateSubscription > ( _registrations . Length + 1 ) ;
57+ UpdateRegistrations ( state ) ;
58+ var subscriptions = new List < ( PersistingComponentStateSubscription , RestoringComponentStateSubscription ) > (
59+ _registrations . Length + 1 ) ;
5860 for ( var i = 0 ; i < _registrations . Length ; i ++ )
5961 {
6062 var registration = _registrations [ i ] ;
@@ -67,20 +69,29 @@ internal void RegisterForPersistence(PersistentComponentState state)
6769 var renderMode = registration . GetRenderModeOrDefault ( ) ;
6870
6971 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 } ) ) ) ;
7584 }
7685
7786 if ( RenderMode != null )
7887 {
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 ) ) ;
8495 }
8596
8697 _subscriptions = subscriptions ;
@@ -92,7 +103,7 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
92103 var accessors = _cachedAccessorsByType . GetOrAdd ( instance . GetType ( ) , static ( runtimeType , declaredType ) => new PropertiesAccessor ( runtimeType , declaredType ) , type ) ;
93104 foreach ( var ( key , propertyType ) in accessors . KeyTypePairs )
94105 {
95- var ( setter , getter ) = accessors . GetAccessor ( key ) ;
106+ var ( setter , getter , options ) = accessors . GetAccessor ( key ) ;
96107 var value = getter . GetValue ( instance ) ;
97108 if ( value != null )
98109 {
@@ -105,33 +116,12 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
105116 "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code" ,
106117 Justification = "Types registered for persistence are preserved in the API call to register them and typically live in assemblies that aren't trimmed." ) ]
107118 [ DynamicDependency ( LinkerFlags . JsonSerialized , typeof ( PersistentServiceRegistration ) ) ]
108- internal void Restore ( PersistentComponentState state )
119+ private void UpdateRegistrations ( PersistentComponentState state )
109120 {
110121 if ( state . TryTakeFromJson < PersistentServiceRegistration [ ] > ( _registryKey , out var registry ) && registry != null )
111122 {
112123 _registrations = ResolveRegistrations ( _registrations . Concat ( registry ) ) ;
113124 }
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- }
135125 }
136126
137127 [ RequiresUnreferencedCode ( "Calls Microsoft.AspNetCore.Components.PersistentComponentState.TryTakeFromJson(String, Type, out Object)" ) ]
@@ -140,9 +130,13 @@ private static void RestoreInstanceState(object instance, Type type, PersistentC
140130 var accessors = _cachedAccessorsByType . GetOrAdd ( instance . GetType ( ) , static ( runtimeType , declaredType ) => new PropertiesAccessor ( runtimeType , declaredType ) , type ) ;
141131 foreach ( var ( key , propertyType ) in accessors . KeyTypePairs )
142132 {
133+ var ( setter , getter , options ) = accessors . GetAccessor ( key ) ;
134+ if ( ! state . CurrentContext . ShouldRestore ( options ) )
135+ {
136+ continue ;
137+ }
143138 if ( state . TryTakeFromJson ( key , propertyType , out var result ) )
144139 {
145- var ( setter , getter ) = accessors . GetAccessor ( key ) ;
146140 setter . SetValue ( instance , result ! ) ;
147141 }
148142 }
@@ -165,12 +159,12 @@ private sealed class PropertiesAccessor
165159 {
166160 internal const BindingFlags BindablePropertyFlags = BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance | BindingFlags . IgnoreCase ;
167161
168- private readonly Dictionary < string , ( PropertySetter , PropertyGetter ) > _underlyingAccessors ;
162+ private readonly Dictionary < string , ( PropertySetter , PropertyGetter , RestoreOptions ) > _underlyingAccessors ;
169163 private readonly ( string , Type ) [ ] _cachedKeysForService ;
170164
171165 public PropertiesAccessor ( [ DynamicallyAccessedMembers ( LinkerFlags . Component ) ] Type targetType , Type keyType )
172166 {
173- _underlyingAccessors = new Dictionary < string , ( PropertySetter , PropertyGetter ) > ( StringComparer . OrdinalIgnoreCase ) ;
167+ _underlyingAccessors = new Dictionary < string , ( PropertySetter , PropertyGetter , RestoreOptions ) > ( StringComparer . OrdinalIgnoreCase ) ;
174168
175169 var keys = new List < ( string , Type ) > ( ) ;
176170 foreach ( var propertyInfo in GetCandidateBindableProperties ( targetType ) )
@@ -204,10 +198,16 @@ public PropertiesAccessor([DynamicallyAccessedMembers(LinkerFlags.Component)] Ty
204198 $ "The type '{ targetType . FullName } ' declares a property matching the name '{ propertyName } ' that is not public. Persistent service properties must be public.") ;
205199 }
206200
201+ var restoreOptions = new RestoreOptions
202+ {
203+ RestoreBehavior = parameterAttribute . RestoreBehavior ,
204+ AllowUpdates = parameterAttribute . AllowUpdates ,
205+ } ;
206+
207207 var propertySetter = new PropertySetter ( targetType , propertyInfo ) ;
208208 var propertyGetter = new PropertyGetter ( targetType , propertyInfo ) ;
209209
210- _underlyingAccessors . Add ( key , ( propertySetter , propertyGetter ) ) ;
210+ _underlyingAccessors . Add ( key , ( propertySetter , propertyGetter , restoreOptions ) ) ;
211211 }
212212
213213 _cachedKeysForService = [ .. keys ] ;
@@ -236,7 +236,7 @@ internal static IEnumerable<PropertyInfo> GetCandidateBindableProperties(
236236 [ DynamicallyAccessedMembers ( LinkerFlags . Component ) ] Type targetType )
237237 => MemberAssignment . GetPropertiesIncludingInherited ( targetType , BindablePropertyFlags ) ;
238238
239- internal ( PropertySetter setter , PropertyGetter getter ) GetAccessor ( string key ) =>
239+ internal ( PropertySetter setter , PropertyGetter getter , RestoreOptions options ) GetAccessor ( string key ) =>
240240 _underlyingAccessors . TryGetValue ( key , out var result ) ? result : default ;
241241 }
242242
0 commit comments