diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs index c966e522be151..69c5382f8dc62 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs @@ -465,9 +465,23 @@ private static void BindInstance( } else { - if (isParentCollection && bindingPoint.Value is null && string.IsNullOrEmpty(configValue)) + // Reaching this point indicates that the configuration section is a leaf node with a string value. + // Typically, configValue will be an empty string if the value in the configuration is empty or null. + // While configValue could be any other string, we already know it cannot be converted to the required type, as TryConvertValue has already failed. + + if (!string.IsNullOrEmpty(configValue)) { - // If we don't have an instance, try to create one + // If we have a value, but no children, we can't bind it to anything + // We already tried calling TryConvertValue and couldn't convert the configuration value to the required type. + if (options.ErrorOnUnknownConfiguration) + { + Debug.Assert(section is not null); + throw new InvalidOperationException(SR.Format(SR.Error_FailedBinding, section.Path, type)); + } + } + else if (isParentCollection && bindingPoint.Value is null) + { + // Try to create the default instance of the type bindingPoint.TrySetValue(CreateInstance(type, config, options, out _)); } } @@ -498,8 +512,6 @@ private static object CreateInstance( BinderOptions options, out ParameterInfo[]? constructorParameters) { - Debug.Assert(!type.IsArray); - constructorParameters = null; if (type.IsInterface || type.IsAbstract) 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 ef822713f367d..95be3ecb5251d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs @@ -2815,5 +2815,22 @@ public void CanGetEnumerableNotCollection() Assert.True(result.Enabled); Assert.Equal(new [] { "new", "class", "rosebud"}, result.Keywords); } + +#if !BUILDING_SOURCE_GENERATOR_TESTS + [Fact] + public void EnsureThrowingWithCollectionAndErrorOnUnknownConfigurationOption() + { + var configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["Values:Monday"] = "not-an-array-of-string" }).Build(); + Assert.Throws(() => configuration.Get(options => options.ErrorOnUnknownConfiguration = true)); + + configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["Values:Monday"] = "" }).Build(); + Assert.Throws(() => configuration.Get(options => options.ErrorOnUnknownConfiguration = true)); + + configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["Values:Monday"] = null }).Build(); + Assert.Throws(() => configuration.Get(options => options.ErrorOnUnknownConfiguration = true)); + } + + internal class TestSettings { public Dictionary Values { get; init; } = []; } +#endif } }