@@ -573,10 +573,389 @@ public class FileOptions
573
573
574
574
---
575
575
576
+ # Performance: IOptionsSnapshot vs IOptionsMonitor
577
+
578
+ <div class =" columns " >
579
+ <div >
580
+
581
+ ## <i class =" fa fa-camera " ></i > IOptionsSnapshot< ; T> ;
582
+
583
+ - ** Scoped lifetime** (per request)
584
+ - ** Recomputed each request**
585
+ - ** Supports named options**
586
+ - ** Best for web apps**
587
+
588
+ ``` csharp
589
+ public class MyController : Controller
590
+ {
591
+ public MyController (IOptionsSnapshot <MyOptions > options )
592
+ {
593
+ // Fresh config per request
594
+ _options = options .Value ;
595
+ }
596
+ }
597
+ ```
598
+
599
+ </div >
600
+ <div >
601
+
602
+ ## <i class =" fa fa-tv " ></i > IOptionsMonitor< ; T> ;
603
+
604
+ - ** Singleton lifetime**
605
+ - ** Real-time change notifications**
606
+ - ** Supports named options**
607
+ - ** Best for background services**
608
+
609
+ ``` csharp
610
+ public class MyService : BackgroundService
611
+ {
612
+ public MyService (IOptionsMonitor <MyOptions > monitor )
613
+ {
614
+ // React to config changes
615
+ monitor .OnChange (OnConfigChanged );
616
+ }
617
+ }
618
+ ```
619
+
620
+ </div >
621
+ </div >
622
+
623
+ ---
624
+
625
+ # Testing Configuration
626
+
627
+ ---
628
+
629
+ # Configuration Testing Patterns
630
+
631
+ <div class =" columns " >
632
+ <div >
633
+
634
+ ## <i class =" fa fa-flask " ></i > Unit Testing
635
+
636
+ ``` csharp
637
+ [Test ]
638
+ public void Service_Uses_Configuration_Correctly ()
639
+ {
640
+ // Arrange
641
+ var config = new ConfigurationBuilder ()
642
+ .AddInMemoryCollection (new []
643
+ {
644
+ new KeyValuePair <string , string >(" ApiUrl" , " https://test.api" ),
645
+ new KeyValuePair <string , string >(" Timeout" , " 30" )
646
+ })
647
+ .Build ();
648
+
649
+ var options = Options .Create (config .Get <ApiOptions >());
650
+ var service = new ApiService (options );
651
+
652
+ // Act & Assert
653
+ Assert .That (service .BaseUrl , Is .EqualTo (" https://test.api" ));
654
+ }
655
+ ```
656
+
657
+ </div >
658
+ <div >
659
+
660
+ ## <i class =" fa fa-cog " ></i > Integration Testing
661
+
662
+ ``` csharp
663
+ public class TestWebApplicationFactory <TProgram >
664
+ : WebApplicationFactory <TProgram > where TProgram : class
665
+ {
666
+ protected override void ConfigureWebHost (IWebHostBuilder builder )
667
+ {
668
+ builder .ConfigureAppConfiguration (config =>
669
+ {
670
+ config .AddInMemoryCollection (new []
671
+ {
672
+ new KeyValuePair <string , string >(" Database:ConnectionString" ,
673
+ " Server=localhost;Database=TestDb;" ),
674
+ new KeyValuePair <string , string >(" ExternalApi:BaseUrl" ,
675
+ " https://mock-api.test" )
676
+ });
677
+ });
678
+ }
679
+ }
680
+ ```
681
+
682
+ </div >
683
+ </div >
684
+
685
+ ---
686
+
687
+ # Configuration Validation
688
+
689
+ <div class =" columns " >
690
+ <div >
691
+
692
+ ## <i class =" fa fa-check-circle " ></i > Data Annotations
693
+
694
+ ``` csharp
695
+ public class DatabaseOptions
696
+ {
697
+ [Required ]
698
+ [Url ]
699
+ public string ConnectionString { get ; set ; } = " " ;
700
+
701
+ [Range (1 , 300 )]
702
+ public int CommandTimeoutSeconds { get ; set ; } = 30 ;
703
+
704
+ [Required ]
705
+ [RegularExpression (@" ^[a-zA-Z0-9_]+$" )]
706
+ public string DatabaseName { get ; set ; } = " " ;
707
+ }
708
+
709
+ // Register with validation
710
+ services .AddOptions <DatabaseOptions >()
711
+ .Bind (configuration .GetSection (" Database" ))
712
+ .ValidateDataAnnotations ()
713
+ .ValidateOnStart ();
714
+ ```
715
+
716
+ </div >
717
+ <div >
718
+
719
+ ## <i class =" fa fa-shield-alt " ></i > Custom Validation
720
+
721
+ ``` csharp
722
+ public class DatabaseOptionsValidator : IValidateOptions <DatabaseOptions >
723
+ {
724
+ public ValidateOptionsResult Validate (string name , DatabaseOptions options )
725
+ {
726
+ var failures = new List <string >();
727
+
728
+ if (string .IsNullOrEmpty (options .ConnectionString ))
729
+ failures .Add (" ConnectionString is required" );
730
+
731
+ if (options .CommandTimeoutSeconds <= 0 )
732
+ failures .Add (" CommandTimeoutSeconds must be positive" );
733
+
734
+ if (! IsValidDatabaseName (options .DatabaseName ))
735
+ failures .Add (" Invalid database name format" );
736
+
737
+ return failures .Count > 0
738
+ ? ValidateOptionsResult .Fail (failures )
739
+ : ValidateOptionsResult .Success ;
740
+ }
741
+ }
742
+
743
+ // Register validator
744
+ services .AddSingleton <IValidateOptions <DatabaseOptions >, DatabaseOptionsValidator >();
745
+ ```
746
+
747
+ </div >
748
+ </div >
749
+
750
+ ---
751
+
752
+ # Validation at Startup
753
+
754
+ <div class =" columns " >
755
+ <div >
756
+
757
+ ## <i class =" fa fa-rocket " ></i > Fail Fast Pattern
758
+
759
+ ``` csharp
760
+ // Program.cs
761
+ var builder = WebApplication .CreateBuilder (args );
762
+
763
+ // Configure options with validation
764
+ builder .Services .AddOptions <ApiOptions >()
765
+ .Bind (builder .Configuration .GetSection (" Api" ))
766
+ .ValidateDataAnnotations ()
767
+ .ValidateOnStart (); // Validates during app startup
768
+
769
+ builder .Services .AddOptions <DatabaseOptions >()
770
+ .Bind (builder .Configuration .GetSection (" Database" ))
771
+ .Validate (options => ! string .IsNullOrEmpty (options .ConnectionString ),
772
+ " Connection string cannot be empty" )
773
+ .ValidateOnStart ();
774
+
775
+ var app = builder .Build ();
776
+ // App fails to start if validation fails
777
+ ```
778
+
779
+ </div >
780
+ <div >
781
+
782
+ ## <i class =" fa fa-exclamation-triangle " ></i > Benefits
783
+
784
+ - ** Early Detection** : Catch configuration errors at startup
785
+ - ** Clear Error Messages** : Know exactly what's wrong
786
+ - ** Prevents Runtime Failures** : No surprises in production
787
+ - ** Better DevEx** : Immediate feedback during development
788
+
789
+ ``` csharp
790
+ // Custom validation method
791
+ services .AddOptions <MyOptions >()
792
+ .Bind (configuration .GetSection (" MySection" ))
793
+ .Validate (options =>
794
+ {
795
+ return options .ApiKey ? .Length >= 10 ;
796
+ }, " ApiKey must be at least 10 characters" )
797
+ .ValidateOnStart ();
798
+ ```
799
+
800
+ </div >
801
+ </div >
802
+
803
+ ---
804
+
576
805
# DEMOS
577
806
578
807
---
579
808
809
+ # Secrets Management Best Practices
810
+
811
+ <div class =" columns " >
812
+ <div >
813
+
814
+ ## <i class =" fa fa-exclamation-triangle " ></i > Don't
815
+
816
+ - Store secrets in appsettings.json
817
+ - Commit secrets to source control
818
+ - Use production secrets in development
819
+ - Log configuration values containing secrets
820
+
821
+ </div >
822
+ <div >
823
+
824
+ ## <i class =" fa fa-check-circle " ></i > Do
825
+
826
+ - Use User Secrets for development
827
+ - Use Azure Key Vault for production
828
+ - Use environment variables for containers
829
+ - Implement proper secret rotation
830
+ - Validate secrets at startup
831
+
832
+ </div >
833
+ </div >
834
+
835
+ ---
836
+
837
+ # Secrets by Environment
838
+
839
+ <div class =" columns3 " >
840
+ <div >
841
+
842
+ ## <i class =" fa fa-laptop-code " ></i > Development
843
+
844
+ - ** User Secrets**
845
+ - Per-project secrets
846
+ - Stored outside source control
847
+ - Easy to manage locally
848
+
849
+ ``` bash
850
+ dotnet user-secrets set " ApiKey" " dev-key-123"
851
+ ```
852
+
853
+ </div >
854
+ <div >
855
+
856
+ ## <i class =" fa fa-server " ></i > Staging/Production
857
+
858
+ - ** Azure Key Vault**
859
+ - Centralized secret management
860
+ - Access policies and RBAC
861
+ - Audit logging
862
+ - Automatic rotation
863
+
864
+ ``` csharp
865
+ builder .Configuration .AddAzureKeyVault (
866
+ keyVaultUrl , credential );
867
+ ```
868
+
869
+ </div >
870
+ <div >
871
+
872
+ ## <i class =" fa fa-cube " ></i > Containers
873
+
874
+ - ** Environment Variables**
875
+ - Kubernetes secrets
876
+ - Docker secrets
877
+ - Service connection strings
878
+
879
+ ``` bash
880
+ docker run -e " ConnectionString=..." myapp
881
+ ```
882
+
883
+ </div >
884
+ </div >
885
+
886
+ ---
887
+
888
+ # Environment-Specific Configuration Strategies
889
+
890
+ <div class =" columns " >
891
+ <div >
892
+
893
+ ## <i class =" fa fa-layer-group " ></i > Layered Configuration
894
+
895
+ ``` csharp
896
+ builder .Configuration
897
+ .AddJsonFile (" appsettings.json" )
898
+ .AddJsonFile ($" appsettings.{env .EnvironmentName }.json" , true )
899
+ .AddEnvironmentVariables ()
900
+ .AddCommandLine (args );
901
+ ```
902
+
903
+ ** Order matters!** Later sources override earlier ones.
904
+
905
+ </div >
906
+ <div >
907
+
908
+ ## <i class =" fa fa-code-branch " ></i > Environment Patterns
909
+
910
+ - ** Development** : User Secrets + local files
911
+ - ** Staging** : Environment variables + Key Vault
912
+ - ** Production** : Key Vault + minimal env vars
913
+ - ** Testing** : In-memory configuration
914
+
915
+ ``` csharp
916
+ if (env .IsDevelopment ())
917
+ {
918
+ builder .Configuration .AddUserSecrets <Program >();
919
+ }
920
+ ```
921
+
922
+ </div >
923
+ </div >
924
+
925
+ ---
926
+
927
+ # Configuration Security Considerations
928
+
929
+ <div class =" columns " >
930
+ <div >
931
+
932
+ ## <i class =" fa fa-shield-alt " ></i > Prevent Secret Leakage
933
+
934
+ - ** Never log IConfiguration directly**
935
+ - ** Redact sensitive values in logs**
936
+ - ** Use IOptionsSnapshot/IOptionsMonitor**
937
+ - ** Implement custom configuration providers for sensitive data**
938
+
939
+ </div >
940
+ <div >
941
+
942
+ ## <i class =" fa fa-eye-slash " ></i > Secure Logging
943
+
944
+ ``` csharp
945
+ // ❌ DON'T - Exposes all configuration
946
+ logger .LogInformation (" Config: {Config}" ,
947
+ JsonSerializer .Serialize (configuration ));
948
+
949
+ // ✅ DO - Log specific, non-sensitive values
950
+ logger .LogInformation (" Database timeout: {Timeout}s" ,
951
+ dbOptions .CommandTimeout );
952
+ ```
953
+
954
+ </div >
955
+ </div >
956
+
957
+ ---
958
+
580
959
# Questions?
581
960
582
961
---
0 commit comments