@@ -17,11 +17,11 @@ namespace Microsoft.AspNetCore.Components.Infrastructure;
1717internal sealed class PersistentServicesRegistry
1818{
1919 private static readonly string _registryKey = typeof ( PersistentServicesRegistry ) . FullName ! ;
20- private static readonly RootTypeCache _persistentServiceTypeCache = new RootTypeCache ( ) ;
20+ private static readonly RootTypeCache _persistentServiceTypeCache = new ( ) ;
2121
2222 private readonly IServiceProvider _serviceProvider ;
2323 private IPersistentServiceRegistration [ ] _registrations ;
24- private List < PersistingComponentStateSubscription > _subscriptions = [ ] ;
24+ private List < ( PersistingComponentStateSubscription , RestoringComponentStateSubscription ) > _subscriptions = [ ] ;
2525 private static readonly ConcurrentDictionary < Type , PropertiesAccessor > _cachedAccessorsByType = new ( ) ;
2626
2727 public PersistentServicesRegistry ( IServiceProvider serviceProvider )
@@ -45,7 +45,9 @@ internal void RegisterForPersistence(PersistentComponentState state)
4545 return ;
4646 }
4747
48- var subscriptions = new List < PersistingComponentStateSubscription > ( _registrations . Length + 1 ) ;
48+ UpdateRegistrations ( state ) ;
49+ var subscriptions = new List < ( PersistingComponentStateSubscription , RestoringComponentStateSubscription ) > (
50+ _registrations . Length + 1 ) ;
4951 for ( var i = 0 ; i < _registrations . Length ; i ++ )
5052 {
5153 var registration = _registrations [ i ] ;
@@ -58,20 +60,29 @@ internal void RegisterForPersistence(PersistentComponentState state)
5860 var renderMode = registration . GetRenderModeOrDefault ( ) ;
5961
6062 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 } ) ) ) ;
6675 }
6776
6877 if ( RenderMode != null )
6978 {
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 ) ) ;
7586 }
7687
7788 _subscriptions = subscriptions ;
@@ -83,7 +94,7 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
8394 var accessors = _cachedAccessorsByType . GetOrAdd ( instance . GetType ( ) , static ( runtimeType , declaredType ) => new PropertiesAccessor ( runtimeType , declaredType ) , type ) ;
8495 foreach ( var ( key , propertyType ) in accessors . KeyTypePairs )
8596 {
86- var ( setter , getter ) = accessors . GetAccessor ( key ) ;
97+ var ( setter , getter , options ) = accessors . GetAccessor ( key ) ;
8798 var value = getter . GetValue ( instance ) ;
8899 if ( value != null )
89100 {
@@ -96,33 +107,12 @@ private static void PersistInstanceState(object instance, Type type, PersistentC
96107 "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code" ,
97108 Justification = "Types registered for persistence are preserved in the API call to register them and typically live in assemblies that aren't trimmed." ) ]
98109 [ DynamicDependency ( LinkerFlags . JsonSerialized , typeof ( PersistentServiceRegistration ) ) ]
99- internal void Restore ( PersistentComponentState state )
110+ private void UpdateRegistrations ( PersistentComponentState state )
100111 {
101112 if ( state . TryTakeFromJson < PersistentServiceRegistration [ ] > ( _registryKey , out var registry ) && registry != null )
102113 {
103114 _registrations = ResolveRegistrations ( _registrations . Concat ( registry ) ) ;
104115 }
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- }
126116 }
127117
128118 [ RequiresUnreferencedCode ( "Calls Microsoft.AspNetCore.Components.PersistentComponentState.TryTakeFromJson(String, Type, out Object)" ) ]
@@ -131,9 +121,13 @@ private static void RestoreInstanceState(object instance, Type type, PersistentC
131121 var accessors = _cachedAccessorsByType . GetOrAdd ( instance . GetType ( ) , static ( runtimeType , declaredType ) => new PropertiesAccessor ( runtimeType , declaredType ) , type ) ;
132122 foreach ( var ( key , propertyType ) in accessors . KeyTypePairs )
133123 {
124+ var ( setter , getter , options ) = accessors . GetAccessor ( key ) ;
125+ if ( ! state . CurrentContext . ShouldRestore ( options ) )
126+ {
127+ continue ;
128+ }
134129 if ( state . TryTakeFromJson ( key , propertyType , out var result ) )
135130 {
136- var ( setter , getter ) = accessors . GetAccessor ( key ) ;
137131 setter . SetValue ( instance , result ! ) ;
138132 }
139133 }
@@ -156,12 +150,12 @@ private sealed class PropertiesAccessor
156150 {
157151 internal const BindingFlags BindablePropertyFlags = BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance | BindingFlags . IgnoreCase ;
158152
159- private readonly Dictionary < string , ( PropertySetter , PropertyGetter ) > _underlyingAccessors ;
153+ private readonly Dictionary < string , ( PropertySetter , PropertyGetter , RestoreOptions ) > _underlyingAccessors ;
160154 private readonly ( string , Type ) [ ] _cachedKeysForService ;
161155
162156 public PropertiesAccessor ( [ DynamicallyAccessedMembers ( LinkerFlags . Component ) ] Type targetType , Type keyType )
163157 {
164- _underlyingAccessors = new Dictionary < string , ( PropertySetter , PropertyGetter ) > ( StringComparer . OrdinalIgnoreCase ) ;
158+ _underlyingAccessors = new Dictionary < string , ( PropertySetter , PropertyGetter , RestoreOptions ) > ( StringComparer . OrdinalIgnoreCase ) ;
165159
166160 var keys = new List < ( string , Type ) > ( ) ;
167161 foreach ( var propertyInfo in GetCandidateBindableProperties ( targetType ) )
@@ -195,10 +189,16 @@ public PropertiesAccessor([DynamicallyAccessedMembers(LinkerFlags.Component)] Ty
195189 $ "The type '{ targetType . FullName } ' declares a property matching the name '{ propertyName } ' that is not public. Persistent service properties must be public.") ;
196190 }
197191
192+ var restoreOptions = new RestoreOptions
193+ {
194+ RestoreBehavior = parameterAttribute . RestoreBehavior ,
195+ AllowUpdates = parameterAttribute . AllowUpdates ,
196+ } ;
197+
198198 var propertySetter = new PropertySetter ( targetType , propertyInfo ) ;
199199 var propertyGetter = new PropertyGetter ( targetType , propertyInfo ) ;
200200
201- _underlyingAccessors . Add ( key , ( propertySetter , propertyGetter ) ) ;
201+ _underlyingAccessors . Add ( key , ( propertySetter , propertyGetter , restoreOptions ) ) ;
202202 }
203203
204204 _cachedKeysForService = [ .. keys ] ;
@@ -227,7 +227,7 @@ internal static IEnumerable<PropertyInfo> GetCandidateBindableProperties(
227227 [ DynamicallyAccessedMembers ( LinkerFlags . Component ) ] Type targetType )
228228 => MemberAssignment . GetPropertiesIncludingInherited ( targetType , BindablePropertyFlags ) ;
229229
230- internal ( PropertySetter setter , PropertyGetter getter ) GetAccessor ( string key ) =>
230+ internal ( PropertySetter setter , PropertyGetter getter , RestoreOptions options ) GetAccessor ( string key ) =>
231231 _underlyingAccessors . TryGetValue ( key , out var result ) ? result : default ;
232232 }
233233
0 commit comments