15
15
16
16
namespace Microsoft . AspNetCore . Components . Infrastructure ;
17
17
18
- internal sealed class PersistentStateValueProvider ( PersistentComponentState state ) : ICascadingValueSupplier
18
+ internal sealed class PersistentStateValueProvider ( PersistentComponentState state , IServiceProvider serviceProvider ) : ICascadingValueSupplier
19
19
{
20
20
private static readonly ConcurrentDictionary < ( string , string , string ) , byte [ ] > _keyCache = new ( ) ;
21
21
private static readonly ConcurrentDictionary < ( Type , string ) , PropertyGetter > _propertyGetterCache = new ( ) ;
22
+ private readonly ConcurrentDictionary < Type , IPersistentComponentStateSerializer ? > _serializerCache = new ( ) ;
22
23
23
24
private readonly Dictionary < ComponentState , PersistingComponentStateSubscription > _subscriptions = [ ] ;
24
25
@@ -42,6 +43,20 @@ public bool CanSupplyValue(in CascadingParameterInfo parameterInfo)
42
43
var componentState = ( ComponentState ) key ! ;
43
44
var storageKey = ComputeKey ( componentState , parameterInfo . PropertyName ) ;
44
45
46
+ // Try to get a custom serializer for this type first
47
+ var customSerializer = _serializerCache . GetOrAdd ( parameterInfo . PropertyType , SerializerFactory ) ;
48
+
49
+ if ( customSerializer != null )
50
+ {
51
+ if ( state . TryTakeBytes ( storageKey , out var data ) )
52
+ {
53
+ var sequence = new ReadOnlySequence < byte > ( data ! ) ;
54
+ return customSerializer . Restore ( parameterInfo . PropertyType , sequence ) ;
55
+ }
56
+ return null ;
57
+ }
58
+
59
+ // Fallback to JSON serialization
45
60
return state . TryTakeFromJson ( storageKey , parameterInfo . PropertyType , out var value ) ? value : null ;
46
61
}
47
62
@@ -52,6 +67,10 @@ public void Subscribe(ComponentState subscriber, in CascadingParameterInfo param
52
67
{
53
68
var propertyName = parameterInfo . PropertyName ;
54
69
var propertyType = parameterInfo . PropertyType ;
70
+
71
+ // Resolve serializer outside the lambda
72
+ var customSerializer = _serializerCache . GetOrAdd ( propertyType , SerializerFactory ) ;
73
+
55
74
_subscriptions [ subscriber ] = state . RegisterOnPersisting ( ( ) =>
56
75
{
57
76
var storageKey = ComputeKey ( subscriber , propertyName ) ;
@@ -61,6 +80,16 @@ public void Subscribe(ComponentState subscriber, in CascadingParameterInfo param
61
80
{
62
81
return Task . CompletedTask ;
63
82
}
83
+
84
+ if ( customSerializer != null )
85
+ {
86
+ using var writer = new PooledArrayBufferWriter < byte > ( ) ;
87
+ customSerializer . Persist ( propertyType , property , writer ) ;
88
+ state . PersistAsBytes ( storageKey , writer . WrittenMemory . ToArray ( ) ) ;
89
+ return Task . CompletedTask ;
90
+ }
91
+
92
+ // Fallback to JSON serialization
64
93
state . PersistAsJson ( storageKey , property , propertyType ) ;
65
94
return Task . CompletedTask ;
66
95
} , subscriber . Renderer . GetComponentRenderMode ( subscriber . Component ) ) ;
@@ -71,6 +100,15 @@ private static PropertyGetter ResolvePropertyGetter(Type type, string propertyNa
71
100
return _propertyGetterCache . GetOrAdd ( ( type , propertyName ) , PropertyGetterFactory ) ;
72
101
}
73
102
103
+ private IPersistentComponentStateSerializer ? SerializerFactory ( Type type )
104
+ {
105
+ var serializerType = typeof ( PersistentComponentStateSerializer < > ) . MakeGenericType ( type ) ;
106
+ var serializer = serviceProvider . GetService ( serializerType ) ;
107
+
108
+ // The generic class now inherits from the internal interface, so we can cast directly
109
+ return serializer as IPersistentComponentStateSerializer ;
110
+ }
111
+
74
112
[ UnconditionalSuppressMessage (
75
113
"Trimming" ,
76
114
"IL2077:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The source field does not have matching annotations." ,
@@ -281,4 +319,49 @@ private static bool IsSerializableKey(object key)
281
319
282
320
return result ;
283
321
}
322
+
323
+ /// <summary>
324
+ /// Serializes <paramref name="instance"/> using the provided <paramref name="serializer"/> and persists it under the given <paramref name="key"/>.
325
+ /// </summary>
326
+ /// <typeparam name="TValue">The <paramref name="instance"/> type.</typeparam>
327
+ /// <param name="key">The key to use to persist the state.</param>
328
+ /// <param name="instance">The instance to persist.</param>
329
+ /// <param name="serializer">The custom serializer to use for serialization.</param>
330
+ internal void PersistAsync < TValue > ( string key , TValue instance , PersistentComponentStateSerializer < TValue > serializer )
331
+ {
332
+ ArgumentNullException . ThrowIfNull ( key ) ;
333
+ ArgumentNullException . ThrowIfNull ( serializer ) ;
334
+
335
+ using var writer = new PooledArrayBufferWriter < byte > ( ) ;
336
+ serializer . Persist ( instance , writer ) ;
337
+ state . PersistAsBytes ( key , writer . WrittenMemory . ToArray ( ) ) ;
338
+ }
339
+
340
+ /// <summary>
341
+ /// Tries to retrieve the persisted state with the given <paramref name="key"/> and deserializes it using the provided <paramref name="serializer"/> into an
342
+ /// instance of type <typeparamref name="TValue"/>.
343
+ /// When the key is present, the state is successfully returned via <paramref name="instance"/>
344
+ /// and removed from the <see cref="PersistentComponentState"/>.
345
+ /// </summary>
346
+ /// <param name="key">The key used to persist the instance.</param>
347
+ /// <param name="serializer">The custom serializer to use for deserialization.</param>
348
+ /// <param name="instance">The persisted instance.</param>
349
+ /// <returns><c>true</c> if the state was found; <c>false</c> otherwise.</returns>
350
+ internal bool TryTake < TValue > ( string key , PersistentComponentStateSerializer < TValue > serializer , [ MaybeNullWhen ( false ) ] out TValue instance )
351
+ {
352
+ ArgumentNullException . ThrowIfNull ( key ) ;
353
+ ArgumentNullException . ThrowIfNull ( serializer ) ;
354
+
355
+ if ( state . TryTakeBytes ( key , out var data ) )
356
+ {
357
+ var sequence = new ReadOnlySequence < byte > ( data ! ) ;
358
+ instance = serializer . Restore ( sequence ) ;
359
+ return true ;
360
+ }
361
+ else
362
+ {
363
+ instance = default ;
364
+ return false ;
365
+ }
366
+ }
284
367
}
0 commit comments