diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs index 09d4f271aba72f..fd395abee35828 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs @@ -457,29 +457,23 @@ private static void BindDictionary( // We only support string and enum keys return; } - + MethodInfo tryGetValue = dictionaryType.GetMethod("TryGetValue")!; PropertyInfo setter = dictionaryType.GetProperty("Item", DeclaredOnlyLookup)!; foreach (IConfigurationSection child in config.GetChildren()) { try { + object key = keyTypeIsEnum ? Enum.Parse(keyType, child.Key) : child.Key; + var args = new object?[] { key, null }; + _ = tryGetValue.Invoke(dictionary, args); object? item = BindInstance( type: valueType, - instance: null, + instance: args[1], config: child, options: options); if (item != null) { - if (keyType == typeof(string)) - { - string key = child.Key; - setter.SetValue(dictionary, item, new object[] { key }); - } - else if (keyTypeIsEnum) - { - object key = Enum.Parse(keyType, child.Key); - setter.SetValue(dictionary, item, new object[] { key }); - } + setter.SetValue(dictionary, item, new object[] { key }); } } catch diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs index 38ab7766d391d6..8f97ce2e7c7f40 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs @@ -483,6 +483,77 @@ public void StringDictionaryBinding() Assert.Equal("val_3", options.StringDictionary["ghi"]); } + [Fact] + public void ShouldPreserveExistingKeysInDictionary() + { + var input = new Dictionary { { "ascii:b", "98" } }; + var config = new ConfigurationBuilder().AddInMemoryCollection(input).Build(); + var origin = new Dictionary { ["a"] = 97 }; + + config.Bind("ascii", origin); + + Assert.Equal(2, origin.Count); + Assert.Equal(97, origin["a"]); + Assert.Equal(98, origin["b"]); + } + + [Fact] + public void ShouldPreserveExistingKeysInNestedDictionary() + { + var input = new Dictionary { ["ascii:b"] = "98" }; + var config = new ConfigurationBuilder().AddInMemoryCollection(input).Build(); + var origin = new Dictionary> + { + ["ascii"] = new Dictionary { ["a"] = 97 } + }; + + config.Bind(origin); + + Assert.Equal(2, origin["ascii"].Count); + Assert.Equal(97, origin["ascii"]["a"]); + Assert.Equal(98, origin["ascii"]["b"]); + } + + [Fact] + public void ShouldPreserveExistingKeysInDictionaryWithEnumAsKeyType() + { + var input = new Dictionary + { + ["abc:def"] = "val_2", + ["abc:ghi"] = "val_3" + }; + var config = new ConfigurationBuilder().AddInMemoryCollection(input).Build(); + var origin = new Dictionary> + { + [KeyEnum.abc] = new Dictionary { [KeyUintEnum.abc] = "val_1" } + }; + + config.Bind(origin); + + Assert.Equal(3, origin[KeyEnum.abc].Count); + Assert.Equal("val_1", origin[KeyEnum.abc][KeyUintEnum.abc]); + Assert.Equal("val_2", origin[KeyEnum.abc][KeyUintEnum.def]); + Assert.Equal("val_3", origin[KeyEnum.abc][KeyUintEnum.ghi]); + } + + [Fact] + public void ShouldPreserveExistingValuesInArrayWhenItIsDictionaryElement() + { + var input = new Dictionary + { + ["ascii:b"] = "98", + }; + var config = new ConfigurationBuilder().AddInMemoryCollection(input).Build(); + var origin = new Dictionary + { + ["ascii"] = new int[] { 97 } + }; + + config.Bind(origin); + + Assert.Equal(new int[] { 97, 98 }, origin["ascii"]); + } + [Fact] public void AlreadyInitializedStringDictionaryBinding() {