From 74a73b1f7d3df450f4b9a0ffd656abb56245c496 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Tue, 6 May 2025 16:07:01 -0700 Subject: [PATCH 1/3] Fix config source gen with Dictionary with nullable Enum value --- .../gen/Emitter/CoreBindingHelpers.cs | 5 +++++ .../ConfigurationBinderTests.TestClasses.cs | 13 +++++++++++++ .../tests/Common/ConfigurationBinderTests.cs | 18 ++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs index ba4f4617d683b2..0e429f406eea77 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs @@ -748,6 +748,11 @@ private void EmitBindCoreImplForDictionary(DictionarySpec type) ParsableFromStringSpec keyType = (ParsableFromStringSpec)_typeIndex.GetEffectiveTypeSpec(type.KeyTypeRef); TypeSpec elementType = _typeIndex.GetTypeSpec(type.ElementTypeRef); + if (elementType is NullableSpec nullableSpec) + { + elementType = _typeIndex.GetTypeSpec(nullableSpec.EffectiveTypeRef); + } + // Parse key EmitBindingLogic( keyType, diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs index 96be1fa6c41642..f6f8e830281199 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs @@ -1131,5 +1131,18 @@ public class ParsableValuesClass public Guid? GuidValue { get; set; } public StringComparison? StringComparisonValue { get; set; } } + + public class OptionsWithDictionaryWithNullableEnumValue + { + // uses MyValue? dictionary values + public Dictionary Dictionary { get; set; } = new(); + } + + public enum MyValue + { + Value1, + Value2, + Value3 + } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs index 95be3ecb5251d2..028e71aee9e2ae 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs @@ -2816,6 +2816,24 @@ public void CanGetEnumerableNotCollection() Assert.Equal(new [] { "new", "class", "rosebud"}, result.Keywords); } + [Fact] + public void TestDictionaryWithNullableEnumValueType() + { + var builder = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "Settings:Dictionary:Key1", "Value2" }, + }); + var config = builder.Build(); + + var settingsSection = config.GetSection("Settings"); + OptionsWithDictionaryWithNullableEnumValue settings = settingsSection.Get()!; + + Assert.NotNull(settings.Dictionary); + Assert.Equal(1, settings.Dictionary.Count); + Assert.Equal(MyValue.Value2, settings.Dictionary["Key1"]); + } + #if !BUILDING_SOURCE_GENERATOR_TESTS [Fact] public void EnsureThrowingWithCollectionAndErrorOnUnknownConfigurationOption() From 1c4ce1a1fb91ee13f14c96144e482ecf0b1e4dd6 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed <10833894+tarekgh@users.noreply.github.com> Date: Tue, 6 May 2025 16:09:44 -0700 Subject: [PATCH 2/3] Update src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../gen/Emitter/CoreBindingHelpers.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs index 0e429f406eea77..ee38f5f02a129b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs @@ -748,6 +748,8 @@ private void EmitBindCoreImplForDictionary(DictionarySpec type) ParsableFromStringSpec keyType = (ParsableFromStringSpec)_typeIndex.GetEffectiveTypeSpec(type.KeyTypeRef); TypeSpec elementType = _typeIndex.GetTypeSpec(type.ElementTypeRef); + // If the element type is a NullableSpec, unwrap it to access the effective type. + // This is necessary for proper configuration binding of nullable enum values. if (elementType is NullableSpec nullableSpec) { elementType = _typeIndex.GetTypeSpec(nullableSpec.EffectiveTypeRef); From aa7cb5300ee1ccaf2280618083c0c1fb908a66c6 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Tue, 6 May 2025 17:42:29 -0700 Subject: [PATCH 3/3] Update the fix to match the collection binding --- .../gen/Emitter/CoreBindingHelpers.cs | 9 +-------- .../tests/Common/ConfigurationBinderTests.TestClasses.cs | 5 ++++- .../tests/Common/ConfigurationBinderTests.cs | 7 ++++++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs index ee38f5f02a129b..03345f3c47770f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs @@ -746,14 +746,7 @@ private void EmitBindCoreImplForDictionary(DictionarySpec type) Emit_Foreach_Section_In_ConfigChildren_StartBlock(); ParsableFromStringSpec keyType = (ParsableFromStringSpec)_typeIndex.GetEffectiveTypeSpec(type.KeyTypeRef); - TypeSpec elementType = _typeIndex.GetTypeSpec(type.ElementTypeRef); - - // If the element type is a NullableSpec, unwrap it to access the effective type. - // This is necessary for proper configuration binding of nullable enum values. - if (elementType is NullableSpec nullableSpec) - { - elementType = _typeIndex.GetTypeSpec(nullableSpec.EffectiveTypeRef); - } + TypeSpec elementType = _typeIndex.GetEffectiveTypeSpec(type.ElementTypeRef); // Parse key EmitBindingLogic( diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs index f6f8e830281199..66d91cc0655304 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs @@ -1132,10 +1132,13 @@ public class ParsableValuesClass public StringComparison? StringComparisonValue { get; set; } } - public class OptionsWithDictionaryWithNullableEnumValue + public class OptionsWithCollectionsWithNullableEnum { // uses MyValue? dictionary values public Dictionary Dictionary { get; set; } = new(); + + // uses MyValue? List values + public List List { get; set; } = new(); } public enum MyValue diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs index 028e71aee9e2ae..f949ea10dc87e5 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs @@ -2823,15 +2823,20 @@ public void TestDictionaryWithNullableEnumValueType() .AddInMemoryCollection(new Dictionary { { "Settings:Dictionary:Key1", "Value2" }, + { "Settings:List:1", "Value3" }, }); var config = builder.Build(); var settingsSection = config.GetSection("Settings"); - OptionsWithDictionaryWithNullableEnumValue settings = settingsSection.Get()!; + OptionsWithCollectionsWithNullableEnum settings = settingsSection.Get()!; Assert.NotNull(settings.Dictionary); Assert.Equal(1, settings.Dictionary.Count); Assert.Equal(MyValue.Value2, settings.Dictionary["Key1"]); + + Assert.NotNull(settings.List); + Assert.Equal(1, settings.List.Count); + Assert.Equal(MyValue.Value3, settings.List[0]); } #if !BUILDING_SOURCE_GENERATOR_TESTS