diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs
index beb45cff274be5..36b5e01ff42b07 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs
@@ -203,6 +203,10 @@ private TypeSpec CreateTypeSpec(TypeParseInfo typeParseInfo)
{
spec = new ConfigurationSectionSpec(type);
}
+ else if (SymbolEqualityComparer.Default.Equals(type, _typeSymbols.IConfiguration))
+ {
+ spec = new ConfigurationSectionSpec(type) { IsIConfiguration = true };
+ }
else if (type is INamedTypeSymbol)
{
spec = CreateObjectSpec(typeParseInfo);
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 42979bf896c101..a4176e76233e45 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs
@@ -126,6 +126,11 @@ private void EmitGetCoreMethod()
EmitEndBlock(); // End if-check for input type.
}
break;
+ case ConfigurationSectionSpec { IsIConfiguration: true }:
+ {
+ _writer.WriteLine($"return {Identifier.configuration};");
+ }
+ break;
case ConfigurationSectionSpec:
{
EmitCastToIConfigurationSection();
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/Types/SimpleTypeSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/Types/SimpleTypeSpec.cs
index 70c7a8042e0359..6171e387c9b28e 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/Types/SimpleTypeSpec.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/Types/SimpleTypeSpec.cs
@@ -13,6 +13,11 @@ public SimpleTypeSpec(ITypeSymbol type) : base(type) { }
internal sealed record ConfigurationSectionSpec : SimpleTypeSpec
{
public ConfigurationSectionSpec(ITypeSymbol type) : base(type) { }
+
+ ///
+ /// Indicates whether this spec represents (as opposed to ).
+ ///
+ public bool IsIConfiguration { get; init; }
}
public sealed record ParsableFromStringSpec : SimpleTypeSpec
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
index 8166b108da3f31..eb1b9c50da0997 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
@@ -324,8 +324,8 @@ private static void BindInstance(
BinderOptions options,
bool isParentCollection)
{
- // if binding IConfigurationSection, break early
- if (type == typeof(IConfigurationSection))
+ // if binding IConfigurationSection or IConfiguration, break early
+ if (type == typeof(IConfigurationSection) || type == typeof(IConfiguration))
{
bindingPoint.TrySetValue(config);
return;
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 78f7cc9e27a554..1fbb3a7ef9172d 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
@@ -60,16 +60,26 @@ public class NestedOptions
}
}
- public class ConfigurationInterfaceOptions
+ public class OptionsWithIConfigurationSection
{
public IConfigurationSection Section { get; set; }
}
+ public class OptionsWithIConfiguration
+ {
+ public IConfiguration Section { get; set; }
+ }
+
public class DerivedOptionsWithIConfigurationSection : DerivedOptions
{
public IConfigurationSection DerivedSection { get; set; }
}
+ public class DerivedOptionsWithIConfiguration : DerivedOptions
+ {
+ public IConfiguration DerivedSection { get; set; }
+ }
+
public record struct RecordStructTypeOptions(string Color, int Length);
public record RecordOptionsWithNesting(int Number, RecordOptionsWithNesting.RecordNestedOptions Nested1,
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 15514263abea49..d5dbe20ae7516b 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs
@@ -95,19 +95,19 @@ public void CanBindIConfigurationSection()
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
- var options = config.Get();
+ var options = config.Get();
var childOptions = options.Section.Get();
Test();
- options = (ConfigurationInterfaceOptions)config.Get(typeof(ConfigurationInterfaceOptions));
+ options = (OptionsWithIConfigurationSection)config.Get(typeof(OptionsWithIConfigurationSection));
childOptions = (DerivedOptions)options.Section.Get(typeof(DerivedOptions));
Test();
- options = config.Get(options => { });
+ options = config.Get(options => { });
childOptions = options.Section.Get(options => { });
Test();
- options = (ConfigurationInterfaceOptions)config.Get(typeof(ConfigurationInterfaceOptions), options => { });
+ options = (OptionsWithIConfigurationSection)config.Get(typeof(OptionsWithIConfigurationSection), options => { });
childOptions = (DerivedOptions)options.Section.Get(typeof(DerivedOptions), options => { });
Test();
@@ -125,7 +125,43 @@ void Test()
}
[Fact]
- public void CanBindWithKeyOverload()
+ public void CanBindIConfigurationSectionWithDerivedOptionsSection()
+ {
+ var dic = new Dictionary
+ {
+ {"Section:Integer", "-2"},
+ {"Section:Boolean", "TRUe"},
+ {"Section:Nested:Integer", "11"},
+ {"Section:Virtual", "Sup"},
+ {"Section:DerivedSection:Nested:Integer", "11"},
+ {"Section:DerivedSection:Virtual", "Sup"}
+ };
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddInMemoryCollection(dic);
+ var config = configurationBuilder.Build();
+
+ var options = config.Get();
+
+ var childOptions = options.Section.Get();
+
+ var childDerivedOptions = childOptions.DerivedSection.Get();
+
+ Assert.True(childOptions.Boolean);
+ Assert.Equal(-2, childOptions.Integer);
+ Assert.Equal(11, childOptions.Nested.Integer);
+ Assert.Equal("Derived:Sup", childOptions.Virtual);
+ Assert.Equal(11, childDerivedOptions.Nested.Integer);
+ Assert.Equal("Derived:Sup", childDerivedOptions.Virtual);
+
+ Assert.Equal("Section", options.Section.Key);
+ Assert.Equal("Section", options.Section.Path);
+ Assert.Equal("DerivedSection", childOptions.DerivedSection.Key);
+ Assert.Equal("Section:DerivedSection", childOptions.DerivedSection.Path);
+ Assert.Null(options.Section.Value);
+ }
+
+ [Fact]
+ public void CanBindIConfiguration()
{
var dic = new Dictionary
{
@@ -138,17 +174,38 @@ public void CanBindWithKeyOverload()
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
- var options = new DerivedOptions();
- config.Bind("Section", options);
+ var options = config.Get();
+ var childOptions = options.Section.Get();
+ Test();
- Assert.True(options.Boolean);
- Assert.Equal(-2, options.Integer);
- Assert.Equal(11, options.Nested.Integer);
- Assert.Equal("Derived:Sup", options.Virtual);
+ options = (OptionsWithIConfiguration)config.Get(typeof(OptionsWithIConfiguration));
+ childOptions = (DerivedOptions)options.Section.Get(typeof(DerivedOptions));
+ Test();
+
+ options = config.Get(options => { });
+ childOptions = options.Section.Get(options => { });
+ Test();
+
+ options = (OptionsWithIConfiguration)config.Get(typeof(OptionsWithIConfiguration), options => { });
+ childOptions = (DerivedOptions)options.Section.Get(typeof(DerivedOptions), options => { });
+ Test();
+
+ void Test()
+ {
+ Assert.True(childOptions.Boolean);
+ Assert.Equal(-2, childOptions.Integer);
+ Assert.Equal(11, childOptions.Nested.Integer);
+ Assert.Equal("Derived:Sup", childOptions.Virtual);
+
+ var section = Assert.IsAssignableFrom(options.Section);
+ Assert.Equal("Section", section.Key);
+ Assert.Equal("Section", section.Path);
+ Assert.Null(section.Value);
+ }
}
[Fact]
- public void CanBindIConfigurationSectionWithDerivedOptionsSection()
+ public void CanBindIConfigurationWithDerivedOptionsSection()
{
var dic = new Dictionary
{
@@ -163,10 +220,8 @@ public void CanBindIConfigurationSectionWithDerivedOptionsSection()
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
- var options = config.Get();
-
- var childOptions = options.Section.Get();
-
+ var options = config.Get();
+ var childOptions = options.Section.Get();
var childDerivedOptions = childOptions.DerivedSection.Get();
Assert.True(childOptions.Boolean);
@@ -176,11 +231,37 @@ public void CanBindIConfigurationSectionWithDerivedOptionsSection()
Assert.Equal(11, childDerivedOptions.Nested.Integer);
Assert.Equal("Derived:Sup", childDerivedOptions.Virtual);
- Assert.Equal("Section", options.Section.Key);
- Assert.Equal("Section", options.Section.Path);
- Assert.Equal("DerivedSection", childOptions.DerivedSection.Key);
- Assert.Equal("Section:DerivedSection", childOptions.DerivedSection.Path);
- Assert.Null(options.Section.Value);
+ var section = Assert.IsAssignableFrom(options.Section);
+ Assert.Equal("Section", section.Key);
+ Assert.Equal("Section", section.Path);
+
+ var derivedSection = Assert.IsAssignableFrom(childOptions.DerivedSection);
+ Assert.Equal("DerivedSection", derivedSection.Key);
+ Assert.Equal("Section:DerivedSection", derivedSection.Path);
+ Assert.Null(section.Value);
+ }
+
+ [Fact]
+ public void CanBindWithKeyOverload()
+ {
+ var dic = new Dictionary
+ {
+ {"Section:Integer", "-2"},
+ {"Section:Boolean", "TRUe"},
+ {"Section:Nested:Integer", "11"},
+ {"Section:Virtual", "Sup"}
+ };
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddInMemoryCollection(dic);
+ var config = configurationBuilder.Build();
+
+ var options = new DerivedOptions();
+ config.Bind("Section", options);
+
+ Assert.True(options.Boolean);
+ Assert.Equal(-2, options.Integer);
+ Assert.Equal(11, options.Nested.Integer);
+ Assert.Equal("Derived:Sup", options.Virtual);
}
[Fact]
@@ -2621,6 +2702,8 @@ public void GetIConfigurationSection()
}
""");
+ Assert.Throws(() => configuration.Get());
+
var obj = configuration.GetSection("value").Get();
Assert.Equal("MyString", obj.Value);
@@ -2649,6 +2732,40 @@ static void ValidateList(List list)
}
}
+ [Fact]
+ public void GetIConfiguration()
+ {
+ var configuration = TestHelpers.GetConfigurationFromJsonString("""
+ {
+ "vaLue": { "key": "MyString" },
+ }
+ """);
+
+ Assert.Same(configuration, configuration.Get());
+
+ var obj = configuration.GetSection("value").Get();
+ Assert.Equal("MyString", obj["key"]);
+
+ configuration = TestHelpers.GetConfigurationFromJsonString("""
+ {
+ "vaLue": [ { "key": "MyString" } ],
+ }
+ """);
+
+ var list = configuration.GetSection("value").Get>();
+ ValidateList(list);
+
+ var dict = configuration.Get>>();
+ Assert.Equal(1, dict.Count);
+ ValidateList(dict["vaLue"]);
+
+ static void ValidateList(List list)
+ {
+ Assert.Equal(1, list.Count);
+ Assert.Equal("MyString", list[0]["key"]);
+ }
+ }
+
[Fact]
public void NullableDictKeys()
{