From 974edf97294f3fb1b74cfddaf0275a657886224b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 10:25:30 -0700 Subject: [PATCH] [release/8.0-rc2] Ensure Bind can handle null from GetSection (#92477) * Ensure Bind can handle null from GetSection IConfiguration instances may return a null value from GetSection. We were not handling this and would throw a NullReferenceException. * Address feedback * Remove Moq from ConfigBinder tests --------- Co-authored-by: Eric StJohn --- .../src/ConfigurationBinder.cs | 5 +++ .../ConfigurationBinderTests.TestClasses.cs | 6 +++ .../tests/Common/ConfigurationBinderTests.cs | 38 ++++++++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs index 74365d8084aa3..dfc35d8020855 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs @@ -298,6 +298,11 @@ private static void BindInstance( return; } + if (config is null) + { + return; + } + var section = config as IConfigurationSection; string? configValue = section?.Value; if (configValue != null && TryConvertValue(type, configValue, section?.Path, out object? convertedValue, out Exception? error)) 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 f47cdbe6dbbb5..48474033ec1f8 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 @@ -888,5 +888,11 @@ public int MyIntProperty } } + public class SimplePoco + { + public string A { get; set; } + public string B { get; set; } + } + } } 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 7c955e789184c..7e635c1f0bdaa 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs @@ -11,6 +11,7 @@ #if BUILDING_SOURCE_GENERATOR_TESTS using Microsoft.Extensions.Configuration; #endif +using Microsoft.Extensions.Configuration.Memory; using Microsoft.Extensions.Configuration.Test; using Xunit; @@ -1767,7 +1768,7 @@ public void EnsureCallingThePropertySetter() Assert.Equal(0, options.OtherCodeNullable); Assert.Equal("default", options.OtherCodeString); Assert.Null(options.OtherCodeNull); - Assert.Null(options.OtherCodeUri); + Assert.Null(options.OtherCodeUri); } [Fact] @@ -2238,7 +2239,7 @@ void TestUntypedOverloads(IConfiguration? configuration, string? key) Assert.Throws(() => configuration.GetValue(typeof(GeolocationClass), key, new GeolocationClass())); Assert.Throws(() => configuration.GetValue(typeof(Geolocation), key)); Assert.Throws(() => configuration.GetValue(typeof(Geolocation), key, defaultValue: null)); - Assert.Throws(() => configuration.GetValue(typeof(Geolocation), key, default(Geolocation))); + Assert.Throws(() => configuration.GetValue(typeof(Geolocation), key, default(Geolocation))); } } @@ -2404,5 +2405,38 @@ public void SharedChildInstance() config.GetSection("A").Bind(instance); Assert.Equal("localhost", instance.ConnectionString); } + + [Fact] + public void CanBindToMockConfigurationSection() + { + const string expectedA = "hello"; + + var configSource = new MemoryConfigurationSource() + { + InitialData = new Dictionary() + { + [$":{nameof(SimplePoco.A)}"] = expectedA, + } + }; + var configRoot = new MockConfigurationRoot(new[] { configSource.Build(null) }); + var configSection = new ConfigurationSection(configRoot, string.Empty); + + SimplePoco result = new(); + configSection.Bind(result); + + Assert.Equal(expectedA, result.A); + Assert.Equal(default(string), result.B); + } + + // a mock configuration root that will return null for undefined Sections, + // as is common when Configuration interfaces are mocked + class MockConfigurationRoot : ConfigurationRoot, IConfigurationRoot + { + public MockConfigurationRoot(IList providers) : base(providers) + { } + + IConfigurationSection IConfiguration.GetSection(string key) => + this[key] is null ? null : new ConfigurationSection(this, key); + } } }