3
3
4
4
using System ;
5
5
using System . Collections . Generic ;
6
+ using System . Linq ;
6
7
using System . Text ;
7
8
using System . Text . Json ;
8
9
using Microsoft . AspNetCore . Components . Infrastructure ;
@@ -431,6 +432,146 @@ public async Task PersistenceFails_MultipleComponentsUseInvalidKeyTypes(object c
431
432
Assert . Contains ( sink . Writes , w => w is { LogLevel : LogLevel . Error } && w . EventId == new EventId ( 1000 , "PersistenceCallbackError" ) ) ;
432
433
}
433
434
435
+ [ Fact ]
436
+ public async Task PersistAsync_CanPersistValueTypes_IntProperty ( )
437
+ {
438
+ // Arrange
439
+ var state = new Dictionary < string , byte [ ] > ( ) ;
440
+ var store = new TestStore ( state ) ;
441
+ var persistenceManager = new ComponentStatePersistenceManager (
442
+ NullLogger < ComponentStatePersistenceManager > . Instance ,
443
+ new ServiceCollection ( ) . BuildServiceProvider ( ) ) ;
444
+
445
+ var renderer = new TestRenderer ( ) ;
446
+ var component = new ValueTypeTestComponent { IntValue = 42 } ;
447
+ var componentStates = CreateComponentState ( renderer , [ ( component , null ) ] , null ) ;
448
+ var componentState = componentStates . First ( ) ;
449
+
450
+ // Create the provider and subscribe the component
451
+ var provider = new SupplyParameterFromPersistentComponentStateValueProvider ( persistenceManager . State ) ;
452
+ var cascadingParameterInfo = CreateCascadingParameterInfo ( nameof ( ValueTypeTestComponent . IntValue ) , typeof ( int ) ) ;
453
+ provider . Subscribe ( componentState , cascadingParameterInfo ) ;
454
+
455
+ // Act
456
+ await persistenceManager . PersistStateAsync ( store , renderer ) ;
457
+
458
+ // Assert
459
+ Assert . NotEmpty ( store . State ) ;
460
+
461
+ // Verify the value was persisted correctly
462
+ var newState = new PersistentComponentState ( new Dictionary < string , byte [ ] > ( ) , [ ] ) ;
463
+ newState . InitializeExistingState ( store . State ) ;
464
+
465
+ var key = SupplyParameterFromPersistentComponentStateValueProvider . ComputeKey ( componentState , cascadingParameterInfo . PropertyName ) ;
466
+ Assert . True ( newState . TryTakeFromJson < int > ( key , out var retrievedValue ) ) ;
467
+ Assert . Equal ( 42 , retrievedValue ) ;
468
+ }
469
+
470
+ [ Fact ]
471
+ public async Task PersistAsync_CanPersistValueTypes_NullableIntProperty ( )
472
+ {
473
+ // Arrange
474
+ var state = new Dictionary < string , byte [ ] > ( ) ;
475
+ var store = new TestStore ( state ) ;
476
+ var persistenceManager = new ComponentStatePersistenceManager (
477
+ NullLogger < ComponentStatePersistenceManager > . Instance ,
478
+ new ServiceCollection ( ) . BuildServiceProvider ( ) ) ;
479
+
480
+ var renderer = new TestRenderer ( ) ;
481
+ var component = new ValueTypeTestComponent { NullableIntValue = 123 } ;
482
+ var componentStates = CreateComponentState ( renderer , [ ( component , null ) ] , null ) ;
483
+ var componentState = componentStates . First ( ) ;
484
+
485
+ // Create the provider and subscribe the component
486
+ var provider = new SupplyParameterFromPersistentComponentStateValueProvider ( persistenceManager . State ) ;
487
+ var cascadingParameterInfo = CreateCascadingParameterInfo ( nameof ( ValueTypeTestComponent . NullableIntValue ) , typeof ( int ? ) ) ;
488
+ provider . Subscribe ( componentState , cascadingParameterInfo ) ;
489
+
490
+ // Act
491
+ await persistenceManager . PersistStateAsync ( store , renderer ) ;
492
+
493
+ // Assert
494
+ Assert . NotEmpty ( store . State ) ;
495
+
496
+ // Verify the value was persisted correctly
497
+ var newState = new PersistentComponentState ( new Dictionary < string , byte [ ] > ( ) , [ ] ) ;
498
+ newState . InitializeExistingState ( store . State ) ;
499
+
500
+ var key = SupplyParameterFromPersistentComponentStateValueProvider . ComputeKey ( componentState , cascadingParameterInfo . PropertyName ) ;
501
+ Assert . True ( newState . TryTakeFromJson < int ? > ( key , out var retrievedValue ) ) ;
502
+ Assert . Equal ( 123 , retrievedValue ) ;
503
+ }
504
+
505
+ [ Fact ]
506
+ public async Task PersistAsync_CanPersistValueTypes_TupleProperty ( )
507
+ {
508
+ // Arrange
509
+ var state = new Dictionary < string , byte [ ] > ( ) ;
510
+ var store = new TestStore ( state ) ;
511
+ var persistenceManager = new ComponentStatePersistenceManager (
512
+ NullLogger < ComponentStatePersistenceManager > . Instance ,
513
+ new ServiceCollection ( ) . BuildServiceProvider ( ) ) ;
514
+
515
+ var renderer = new TestRenderer ( ) ;
516
+ var component = new ValueTypeTestComponent { TupleValue = ( "test" , 456 ) } ;
517
+ var componentStates = CreateComponentState ( renderer , [ ( component , null ) ] , null ) ;
518
+ var componentState = componentStates . First ( ) ;
519
+
520
+ // Create the provider and subscribe the component
521
+ var provider = new SupplyParameterFromPersistentComponentStateValueProvider ( persistenceManager . State ) ;
522
+ var cascadingParameterInfo = CreateCascadingParameterInfo ( nameof ( ValueTypeTestComponent . TupleValue ) , typeof ( ( string , int ) ) ) ;
523
+ provider . Subscribe ( componentState , cascadingParameterInfo ) ;
524
+
525
+ // Act
526
+ await persistenceManager . PersistStateAsync ( store , renderer ) ;
527
+
528
+ // Assert
529
+ Assert . NotEmpty ( store . State ) ;
530
+
531
+ // Verify the value was persisted correctly
532
+ var newState = new PersistentComponentState ( new Dictionary < string , byte [ ] > ( ) , [ ] ) ;
533
+ newState . InitializeExistingState ( store . State ) ;
534
+
535
+ var key = SupplyParameterFromPersistentComponentStateValueProvider . ComputeKey ( componentState , cascadingParameterInfo . PropertyName ) ;
536
+ Assert . True ( newState . TryTakeFromJson < ( string , int ) > ( key , out var retrievedValue ) ) ;
537
+ Assert . Equal ( ( "test" , 456 ) , retrievedValue ) ;
538
+ }
539
+
540
+ [ Fact ]
541
+ public async Task PersistAsync_CanPersistValueTypes_NullableTupleProperty ( )
542
+ {
543
+ // Arrange
544
+ var state = new Dictionary < string , byte [ ] > ( ) ;
545
+ var store = new TestStore ( state ) ;
546
+ var persistenceManager = new ComponentStatePersistenceManager (
547
+ NullLogger < ComponentStatePersistenceManager > . Instance ,
548
+ new ServiceCollection ( ) . BuildServiceProvider ( ) ) ;
549
+
550
+ var renderer = new TestRenderer ( ) ;
551
+ var component = new ValueTypeTestComponent { NullableTupleValue = ( "test2" , 789 ) } ;
552
+ var componentStates = CreateComponentState ( renderer , [ ( component , null ) ] , null ) ;
553
+ var componentState = componentStates . First ( ) ;
554
+
555
+ // Create the provider and subscribe the component
556
+ var provider = new SupplyParameterFromPersistentComponentStateValueProvider ( persistenceManager . State ) ;
557
+ var cascadingParameterInfo = CreateCascadingParameterInfo ( nameof ( ValueTypeTestComponent . NullableTupleValue ) , typeof ( ( string , int ) ? ) ) ;
558
+ provider . Subscribe ( componentState , cascadingParameterInfo ) ;
559
+
560
+ // Act
561
+ await persistenceManager . PersistStateAsync ( store , renderer ) ;
562
+
563
+ // Assert
564
+ Assert . NotEmpty ( store . State ) ;
565
+
566
+ // Verify the value was persisted correctly
567
+ var newState = new PersistentComponentState ( new Dictionary < string , byte [ ] > ( ) , [ ] ) ;
568
+ newState . InitializeExistingState ( store . State ) ;
569
+
570
+ var key = SupplyParameterFromPersistentComponentStateValueProvider . ComputeKey ( componentState , cascadingParameterInfo . PropertyName ) ;
571
+ Assert . True ( newState . TryTakeFromJson < ( string , int ) ? > ( key , out var retrievedValue ) ) ;
572
+ Assert . Equal ( ( "test2" , 789 ) , retrievedValue ) ;
573
+ }
574
+
434
575
private static void InitializeState ( PersistentComponentState state , List < ( ComponentState componentState , string propertyName , string value ) > items )
435
576
{
436
577
var dictionary = new Dictionary < string , byte [ ] > ( ) ;
@@ -452,7 +593,7 @@ private static CascadingParameterInfo CreateCascadingParameterInfo(string proper
452
593
453
594
private static List < ComponentState > CreateComponentState (
454
595
TestRenderer renderer ,
455
- List < ( TestComponent , object ) > components ,
596
+ List < ( IComponent , object ) > components ,
456
597
ParentComponent parentComponent = null )
457
598
{
458
599
var i = 1 ;
@@ -464,7 +605,20 @@ private static List<ComponentState> CreateComponentState(
464
605
var componentState = new ComponentState ( renderer , i ++ , component , parentComponentState ) ;
465
606
if ( currentRenderTree != null && key != null )
466
607
{
467
- currentRenderTree . OpenComponent < TestComponent > ( 0 ) ;
608
+ // Open component based on the actual component type
609
+ if ( component is TestComponent )
610
+ {
611
+ currentRenderTree . OpenComponent < TestComponent > ( 0 ) ;
612
+ }
613
+ else if ( component is ValueTypeTestComponent )
614
+ {
615
+ currentRenderTree . OpenComponent < ValueTypeTestComponent > ( 0 ) ;
616
+ }
617
+ else
618
+ {
619
+ currentRenderTree . OpenComponent < IComponent > ( 0 ) ;
620
+ }
621
+
468
622
var frames = currentRenderTree . GetFrames ( ) ;
469
623
frames . Array [ frames . Count - 1 ] . ComponentStateField = componentState ;
470
624
if ( key != null )
@@ -497,6 +651,24 @@ private class TestComponent : IComponent
497
651
public Task SetParametersAsync ( ParameterView parameters ) => throw new NotImplementedException ( ) ;
498
652
}
499
653
654
+ private class ValueTypeTestComponent : IComponent
655
+ {
656
+ [ SupplyParameterFromPersistentComponentState ]
657
+ public int IntValue { get ; set ; }
658
+
659
+ [ SupplyParameterFromPersistentComponentState ]
660
+ public int ? NullableIntValue { get ; set ; }
661
+
662
+ [ SupplyParameterFromPersistentComponentState ]
663
+ public ( string , int ) TupleValue { get ; set ; }
664
+
665
+ [ SupplyParameterFromPersistentComponentState ]
666
+ public ( string , int ) ? NullableTupleValue { get ; set ; }
667
+
668
+ public void Attach ( RenderHandle renderHandle ) => throw new NotImplementedException ( ) ;
669
+ public Task SetParametersAsync ( ParameterView parameters ) => throw new NotImplementedException ( ) ;
670
+ }
671
+
500
672
private class TestStore ( Dictionary < string , byte [ ] > initialState ) : IPersistentComponentStateStore
501
673
{
502
674
public IDictionary < string , byte [ ] > State { get ; set ; } = initialState ;
0 commit comments