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 03345f3c47770f..8001e8f9175c0f 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs
@@ -115,14 +115,15 @@ private void EmitGetCoreMethod()
case ParsableFromStringSpec stringParsableType:
{
EmitCastToIConfigurationSection();
+
+ EmitStartBlock($"if ({Identifier.TryGetConfigurationValue}({Identifier.configuration}, {Identifier.key}: null, out string? {Identifier.value}))");
EmitBindingLogic(
stringParsableType,
- Expression.sectionValue,
+ Identifier.value,
Expression.sectionPath,
writeOnSuccess: parsedValueExpr => _writer.WriteLine($"return {parsedValueExpr};"),
- checkForNullSectionValue: stringParsableType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue,
- defaultValueSource: null,
- useIncrementalStringValueIdentifier: false);
+ checkForNullSectionValue: stringParsableType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue);
+ EmitEndBlock(); // End if-check for input type.
}
break;
case ConfigurationSectionSpec:
@@ -183,14 +184,7 @@ private void EmitGetValueCoreMethod()
_writer.WriteLine($@"{Identifier.IConfigurationSection} {Identifier.section} = {GetSectionFromConfigurationExpression(Identifier.key, addQuotes: false)};");
_writer.WriteLine();
- _writer.WriteLine($$"""
- if ({{Expression.sectionValue}} is not string {{Identifier.value}})
- {
- return null;
- }
- """);
-
- _writer.WriteLine();
+ EmitStartBlock($"if ({Identifier.TryGetConfigurationValue}({Identifier.section}, {Identifier.key}: null, out string? {Identifier.value}) && !string.IsNullOrEmpty({Identifier.value}))");
bool isFirstType = true;
foreach (TypeSpec type in targetTypes)
@@ -203,12 +197,11 @@ private void EmitGetValueCoreMethod()
Identifier.value,
Expression.sectionPath,
writeOnSuccess: (parsedValueExpr) => _writer.WriteLine($"return {parsedValueExpr};"),
- checkForNullSectionValue: false,
- defaultValueSource: null,
- useIncrementalStringValueIdentifier: false);
+ checkForNullSectionValue: false);
EmitEndBlock();
}
+ EmitEndBlock();
_writer.WriteLine();
_writer.WriteLine("return null;");
@@ -420,7 +413,8 @@ void EmitBindImplForMember(MemberSpec member)
if (member is ParameterSpec parameter && parameter.ErrorOnFailedBinding)
{
// Add exception logic for parameter ctors; must be present in configuration object.
- EmitThrowBlock(condition: "else");
+ // In case of Arrays, we emit extra block to handle empty arrays. The throw block will not be `else` case at that time.
+ EmitThrowBlock(condition: _typeIndex.GetEffectiveTypeSpec(member.TypeRef) is ArraySpec ? $"if ({member.Name} is null)" : "else");
}
_writer.WriteLine();
@@ -438,6 +432,9 @@ void EmitThrowBlock(string condition) =>
private void EmitHelperMethods()
{
+ // This is used all the time Get, Bind, and GetValue methods.
+ EmitTryGetConfigurationValueMethod();
+
// Emitted if we are to bind objects with complex members, or if we're emitting BindCoreMain or GetCore methods.
bool emitAsConfigWithChildren = ShouldEmitMethods(MethodsToGen_CoreBindingHelper.AsConfigWithChildren);
@@ -488,6 +485,24 @@ private void EmitHelperMethods()
}
}
+ private void EmitTryGetConfigurationValueMethod()
+ {
+ EmitBlankLineIfRequired();
+ _writer.WriteLine($$"""
+ /// Tries to get the configuration value for the specified key.
+ public static bool {{Identifier.TryGetConfigurationValue}}({{Identifier.IConfiguration}} {{Identifier.configuration}}, string {{Identifier.key}}, out string? {{Identifier.value}})
+ {
+ if ({{Identifier.configuration}} is {{Identifier.ConfigurationSection}} {{Identifier.section}})
+ {
+ return {{Identifier.section}}.TryGetValue({{Identifier.key}}, out {{Identifier.value}});
+ }
+
+ {{Identifier.value}} = {{Identifier.key}} != null ? {{Identifier.configuration}}[{{Identifier.key}}] : {{Identifier.configuration}} is {{Identifier.IConfigurationSection}} sec ? sec.Value : null;
+ return {{Identifier.value}} != null;
+ }
+ """);
+ }
+
private void EmitValidateConfigurationKeysMethod()
{
const string keysIdentifier = "keys";
@@ -572,7 +587,7 @@ private void EmitGetBinderOptionsHelper()
private void EmitEnumParseMethod()
{
- string exceptionArg1 = string.Format(ExceptionMessages.FailedBinding, $"{{{Identifier.path}}}", $"{{typeof(T)}}");
+ string exceptionArg1 = string.Format(ExceptionMessages.FailedBinding, $"{{{Identifier.value} ?? \"null\"}}", $"{{{Identifier.path}}}", $"{{typeof(T)}}");
string parseEnumCall = _emitGenericParseEnum ? "Enum.Parse(value, ignoreCase: true)" : "(T)Enum.Parse(typeof(T), value, ignoreCase: true)";
_writer.WriteLine($$"""
@@ -642,7 +657,7 @@ private void EmitPrimitiveParseMethod(ParsableFromStringSpec type)
}
}
- string exceptionArg1 = string.Format(ExceptionMessages.FailedBinding, $"{{{Identifier.path}}}", $"{{typeof({typeFQN})}}");
+ string exceptionArg1 = string.Format(ExceptionMessages.FailedBinding, $"{{{Identifier.value} ?? \"null\"}}", $"{{{Identifier.path}}}", $"{{typeof({typeFQN})}}");
EmitStartBlock($"public static {typeFQN} {TypeIndex.GetParseMethodName(type)}(string {Identifier.value}, string? {Identifier.path})");
EmitEndBlock($$"""
@@ -695,14 +710,14 @@ private void EmitBindingLogicForEnumerableWithAdd(TypeRef elementTypeRef, string
{
case ParsableFromStringSpec stringParsableType:
{
+ EmitStartBlock($"if ({Identifier.TryGetConfigurationValue}({Identifier.section}, {Identifier.key}: null, out string? {Identifier.value}))");
EmitBindingLogic(
stringParsableType,
- Expression.sectionValue,
+ Identifier.value,
Expression.sectionPath,
(parsedValueExpr) => _writer.WriteLine($"{addExpr}({parsedValueExpr});"),
- checkForNullSectionValue: true,
- defaultValueSource: null,
- useIncrementalStringValueIdentifier: false);
+ checkForNullSectionValue: true);
+ EmitEndBlock(); // End if-check for input type.
}
break;
case ConfigurationSectionSpec:
@@ -754,9 +769,7 @@ private void EmitBindCoreImplForDictionary(DictionarySpec type)
Expression.sectionKey,
Expression.sectionPath,
Emit_BindAndAddLogic_ForElement,
- checkForNullSectionValue: false,
- defaultValueSource: null,
- useIncrementalStringValueIdentifier: false);
+ checkForNullSectionValue: false);
void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr)
{
@@ -764,14 +777,14 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr)
{
case ParsableFromStringSpec stringParsableElementType:
{
+ EmitStartBlock($"if ({Identifier.TryGetConfigurationValue}({Identifier.section}, {Identifier.key}: null, out string? {Identifier.value}))");
EmitBindingLogic(
stringParsableElementType,
- Expression.sectionValue,
+ Identifier.value,
Expression.sectionPath,
writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{instanceIdentifier}[{parsedKeyExpr}] = {parsedValueExpr};"),
- checkForNullSectionValue: true,
- defaultValueSource: null,
- useIncrementalStringValueIdentifier: false);
+ checkForNullSectionValue: true);
+ EmitEndBlock(); // End if-check for input type.
}
break;
case ConfigurationSectionSpec:
@@ -877,14 +890,54 @@ private bool EmitBindImplForMember(
if (canSet && canGet)
{
EmitBlankLineIfRequired();
+ string valueIdentifier = GetIncrementalIdentifier(Identifier.value);
+ EmitStartBlock($@"if ({Identifier.TryGetConfigurationValue}({Identifier.configuration}, {Identifier.key}: ""{member.ConfigurationKeyName}"", out string? {valueIdentifier}))");
+
+ // Decide to emit the null check block for nullable types (e.g. int?).
+ // We don't emit this block for types that can be assigned directly from IConfigurationSection.Value as the valueIdentifier value can assigned
+ // anyway to the memberAccessExpr regardless of the nullability. This can reduce the emitted code size when assigning objects or strings which
+ // are common cases.
+ bool emitNullCheck = member.TypeRef.CanBeNull && stringParsableType.StringParsableTypeKind != StringParsableTypeKind.AssignFromSectionValue;
+
+ // Nullable type can be set to null
+ if (emitNullCheck)
+ {
+ EmitStartBlock($"if ({valueIdentifier} is null)");
+ _writer.WriteLine($"{memberAccessExpr} = null;");
+ EmitEndBlock(); // End if-check for input type.
+ EmitStartBlock($"else");
+ }
+
EmitBindingLogic(
stringParsableType,
- $@"{Identifier.configuration}[""{member.ConfigurationKeyName}""]",
+ valueIdentifier,
sectionPathExpr,
writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{memberAccessExpr} = {parsedValueExpr};"),
- checkForNullSectionValue: true,
- defaultValueSource: initializationKind == InitializationKind.Declaration ? memberAccessExpr : null,
- useIncrementalStringValueIdentifier: true);
+ checkForNullSectionValue: true);
+
+ if (emitNullCheck)
+ {
+ EmitEndBlock(); // end of $"if ({valueIdentifier} is null)"
+ }
+
+ EmitEndBlock(); // End if-check for input type.
+
+ if (initializationKind == InitializationKind.Declaration)
+ {
+ EmitStartBlock($"else if (defaultValueIfNotFound)");
+ if (!stringParsableType.TypeRef.CanBeNull)
+ {
+ _writer.WriteLine($"{memberAccessExpr} = {memberAccessExpr};");
+ }
+ else
+ {
+ _writer.WriteLine($"var currentValue = {memberAccessExpr};");
+ EmitStartBlock($"if (currentValue is not null)");
+ _writer.WriteLine($"{memberAccessExpr} = currentValue;");
+ EmitEndBlock();
+ }
+ EmitEndBlock();
+ }
}
return true;
@@ -910,14 +963,27 @@ complexType is not CollectionSpec &&
return false;
}
- string sectionValidationCall = $"{MethodsToGen_CoreBindingHelper.AsConfigWithChildren}({sectionParseExpr})";
+ EmitBlankLineIfRequired();
+ string configSection = GetIncrementalIdentifier(Identifier.value);
+ _writer.WriteLine($"var {configSection} = {sectionParseExpr};");
+
+ string sectionValidationCall = $"{MethodsToGen_CoreBindingHelper.AsConfigWithChildren}({configSection})";
string sectionIdentifier = GetIncrementalIdentifier(Identifier.section);
- EmitBlankLineIfRequired();
EmitStartBlock($"if ({sectionValidationCall} is {Identifier.IConfigurationSection} {sectionIdentifier})");
EmitBindingLogicForComplexMember(member, memberAccessExpr, sectionIdentifier, canSet);
EmitEndBlock();
+ // The current configuration section doesn't have any children, let's check if we are binding to an array and the configuration value is empty string.
+ // In this case, we will assign an empty array to the member. Otherwise, we will skip the binding logic.
+ if (complexType is ArraySpec arraySpec && canSet)
+ {
+ string valueIdentifier = GetIncrementalIdentifier(Identifier.value);
+ EmitStartBlock($@"if ({memberAccessExpr} is null && {Identifier.TryGetConfigurationValue}({configSection}, {Identifier.key}: null, out string? {valueIdentifier}) && {valueIdentifier} == string.Empty)");
+ _writer.WriteLine($"{memberAccessExpr} = global::System.{Identifier.Array}.Empty<{arraySpec.ElementTypeRef.FullyQualifiedName}>();");
+ EmitEndBlock();
+ }
+
return _typeIndex.CanInstantiate(complexType);
}
default:
@@ -1096,51 +1162,39 @@ private void EmitBindingLogic(
string sectionValueExpr,
string sectionPathExpr,
Action? writeOnSuccess,
- bool checkForNullSectionValue,
- string? defaultValueSource,
- bool useIncrementalStringValueIdentifier)
+ bool checkForNullSectionValue)
{
StringParsableTypeKind typeKind = type.StringParsableTypeKind;
Debug.Assert(typeKind is not StringParsableTypeKind.None);
- string nonNull_StringValue_Identifier = useIncrementalStringValueIdentifier ? GetIncrementalIdentifier(Identifier.value) : Identifier.value;
- string stringValueToParse_Expr = checkForNullSectionValue ? nonNull_StringValue_Identifier : sectionValueExpr;
string parsedValueExpr = typeKind switch
{
- StringParsableTypeKind.AssignFromSectionValue => stringValueToParse_Expr,
- StringParsableTypeKind.Enum => $"ParseEnum<{type.TypeRef.FullyQualifiedName}>({stringValueToParse_Expr}, {sectionPathExpr})",
- _ => $"{TypeIndex.GetParseMethodName(type)}({stringValueToParse_Expr}, {sectionPathExpr})",
+ StringParsableTypeKind.AssignFromSectionValue => sectionValueExpr,
+ StringParsableTypeKind.Enum => $"ParseEnum<{type.TypeRef.FullyQualifiedName}>({sectionValueExpr}, {sectionPathExpr})",
+ _ => $"{TypeIndex.GetParseMethodName(type)}({sectionValueExpr}, {sectionPathExpr})",
};
- if (!checkForNullSectionValue)
+ // Usually assigning the configuration value to string or object
+ if (!checkForNullSectionValue || typeKind == StringParsableTypeKind.AssignFromSectionValue)
{
writeOnSuccess?.Invoke(parsedValueExpr);
}
else
{
- // In case of calling parsing methods, check the section value first for null or empty before calling parse.
- string extraCondition = typeKind == StringParsableTypeKind.AssignFromSectionValue ? "" : $" && !string.IsNullOrEmpty({nonNull_StringValue_Identifier})";
- EmitStartBlock($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier}{extraCondition})");
- writeOnSuccess?.Invoke(parsedValueExpr);
- EmitEndBlock();
- }
-
- if (defaultValueSource is not null)
- {
- Debug.Assert(checkForNullSectionValue);
+ // call parsing methods
- EmitStartBlock($"else if (defaultValueIfNotFound)");
- if (!type.TypeRef.CanBeNull)
- {
- writeOnSuccess?.Invoke(defaultValueSource);
- }
- else
+ string conditionPrefix = string.Empty;
+ // Special case ByteArray when having empty string configuration value as we need to assign empty byte array at that time.
+ if (typeKind == StringParsableTypeKind.ByteArray)
{
- _writer.WriteLine($"var currentValue = {defaultValueSource};");
- EmitStartBlock($"if (currentValue is not null)");
- writeOnSuccess?.Invoke("currentValue");
+ EmitStartBlock($"if ({sectionValueExpr} == string.Empty)");
+ writeOnSuccess?.Invoke(parsedValueExpr);
EmitEndBlock();
+ conditionPrefix = "else ";
}
+
+ EmitStartBlock($"{conditionPrefix}if (!string.IsNullOrEmpty({sectionValueExpr}))");
+ writeOnSuccess?.Invoke(parsedValueExpr);
EmitEndBlock();
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/ExceptionMessages.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/ExceptionMessages.cs
index 696af27d697374..beb0b564bb7f9d 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/ExceptionMessages.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/ExceptionMessages.cs
@@ -13,7 +13,7 @@ internal static class ExceptionMessages
public const string CannotBindToConstructorParameter = "Cannot create instance of type '{0}' because one or more parameters cannot be bound to. Constructor parameters cannot be declared as in, out, or ref. Invalid parameters are: '{1}'";
public const string CannotSpecifyBindNonPublicProperties = "The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.";
public const string ConstructorParametersDoNotMatchProperties = "Cannot create instance of type '{0}' because one or more parameters cannot be bound to. Constructor parameters must have corresponding properties. Fields are not supported. Missing properties are: '{1}'";
- public const string FailedBinding = "Failed to convert configuration value at '{0}' to type '{1}'.";
+ public const string FailedBinding = "Failed to convert configuration value '{0}' at '{1}' to type '{2}'.";
public const string MissingConfig = "'{0}' was set on the provided {1}, but the following properties were not found on the instance of {2}: {3}";
public const string MissingPublicInstanceConstructor = "Cannot create instance of type '{0}' because it is missing a public instance constructor.";
public const string MultipleParameterizedConstructors = "Cannot create instance of type '{0}' because it has multiple public parameterized constructors.";
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/Helpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/Helpers.cs
index 9d340ac0e93c55..cf9511cf5a6f90 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/Helpers.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/Helpers.cs
@@ -114,6 +114,7 @@ private static class Identifier
public const string HasValue = nameof(HasValue);
public const string IConfiguration = nameof(IConfiguration);
public const string IConfigurationSection = nameof(IConfigurationSection);
+ public const string ConfigurationSection = nameof(ConfigurationSection);
public const string Int32 = "int";
public const string InterceptsLocation = nameof(InterceptsLocation);
public const string InvalidOperationException = nameof(InvalidOperationException);
@@ -133,6 +134,7 @@ private static class Identifier
public const string Type = nameof(Type);
public const string Uri = nameof(Uri);
public const string ValidateConfigurationKeys = nameof(ValidateConfigurationKeys);
+ public const string TryGetConfigurationValue = nameof(TryGetConfigurationValue);
public const string Value = nameof(Value);
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
index f5c6c05ba9616e..a34553e73cd3d2 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs
@@ -311,7 +311,8 @@ private static void BindProperty(PropertyInfo property, object instance, IConfig
// For property binding, there are some cases when HasNewValue is not set in BindingPoint while a non-null Value inside that object can be retrieved from the property getter.
// As example, when binding a property which not having a configuration entry matching this property and the getter can initialize the Value.
// It is important to call the property setter as the setters can have a logic adjusting the Value.
- if (!propertyBindingPoint.IsReadOnly && propertyBindingPoint.Value is not null)
+ // Otherwise, if the HasNewValue set to true, it means that the property setter should be called anyway as encountering a new value.
+ if (!propertyBindingPoint.IsReadOnly && (propertyBindingPoint.Value is not null || propertyBindingPoint.HasNewValue))
{
property.SetValue(instance, propertyBindingPoint.Value);
}
@@ -338,15 +339,41 @@ private static void BindInstance(
return;
}
- var section = config as IConfigurationSection;
- string? configValue = section?.Value;
- if (configValue != null && TryConvertValue(type, configValue, section?.Path, out object? convertedValue, out Exception? error))
+ IConfigurationSection? section;
+ string? configValue;
+ bool isConfigurationExist;
+
+ if (config is ConfigurationSection configSection)
+ {
+ section = configSection;
+ isConfigurationExist = configSection.TryGetValue(key:null, out configValue);
+ }
+ else
+ {
+ section = config as IConfigurationSection;
+ configValue = section?.Value;
+ isConfigurationExist = configValue != null;
+ }
+
+ if (isConfigurationExist && TryConvertValue(type, configValue, section?.Path, out object? convertedValue, out Exception? error))
{
if (error != null)
{
throw error;
}
+ if (type == typeof(byte[]) && bindingPoint.Value is byte[] byteArray && byteArray.Length > 0)
+ {
+ if (convertedValue is byte[] convertedByteArray && convertedByteArray.Length > 0)
+ {
+ Array a = Array.CreateInstance(type.GetElementType()!, byteArray.Length + convertedByteArray.Length);
+ Array.Copy(byteArray, a, byteArray.Length);
+ Array.Copy(convertedByteArray, 0, a, byteArray.Length, convertedByteArray.Length);
+ bindingPoint.TrySetValue(a);
+ }
+ return;
+ }
+
// Leaf nodes are always reinitialized
bindingPoint.TrySetValue(convertedValue);
return;
@@ -476,13 +503,29 @@ private static void BindInstance(
if (options.ErrorOnUnknownConfiguration)
{
Debug.Assert(section is not null);
- throw new InvalidOperationException(SR.Format(SR.Error_FailedBinding, section.Path, type));
+ throw new InvalidOperationException(SR.Format(SR.Error_FailedBinding, configValue, section.Path, type));
}
}
- else if (isParentCollection && bindingPoint.Value is null)
+ else
{
- // Try to create the default instance of the type
- bindingPoint.TrySetValue(CreateInstance(type, config, options, out _));
+ if (isParentCollection && bindingPoint.Value is null)
+ {
+ // Try to create the default instance of the type
+ bindingPoint.TrySetValue(CreateInstance(type, config, options, out _));
+ }
+ else if (isConfigurationExist && bindingPoint.Value is null)
+ {
+ // Don't override the existing array in bindingPoint.Value if it is already set.
+ if (type.IsArray || IsImmutableArrayCompatibleInterface(type))
+ {
+ // When having configuration value set to empty string, we create an empty array
+ bindingPoint.TrySetValue(configValue is null ? null : Array.CreateInstance(type.GetElementType()!, 0));
+ }
+ else
+ {
+ bindingPoint.TrySetValue(bindingPoint.Value); // force setting null value
+ }
+ }
}
}
}
@@ -930,7 +973,7 @@ private static Array BindArray(Type type, IEnumerable? source, IConfiguration co
private static bool TryConvertValue(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
Type type,
- string value, string? path, out object? result, out Exception? error)
+ string? value, string? path, out object? result, out Exception? error)
{
error = null;
result = null;
@@ -954,11 +997,14 @@ private static bool TryConvertValue(
{
try
{
- result = converter.ConvertFromInvariantString(value);
+ if (value is not null)
+ {
+ result = converter.ConvertFromInvariantString(value);
+ }
}
catch (Exception ex)
{
- error = new InvalidOperationException(SR.Format(SR.Error_FailedBinding, path, type), ex);
+ error = new InvalidOperationException(SR.Format(SR.Error_FailedBinding, value, path, type), ex);
}
return true;
}
@@ -967,11 +1013,14 @@ private static bool TryConvertValue(
{
try
{
- result = Convert.FromBase64String(value);
+ if (value is not null )
+ {
+ result = value == string.Empty ? Array.Empty() : Convert.FromBase64String(value);
+ }
}
catch (FormatException ex)
{
- error = new InvalidOperationException(SR.Format(SR.Error_FailedBinding, path, type), ex);
+ error = new InvalidOperationException(SR.Format(SR.Error_FailedBinding, value, path, type), ex);
}
return true;
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj
index 4fdca78c2bcdde..1b594196a3a8ee 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Microsoft.Extensions.Configuration.Binder.csproj
@@ -33,6 +33,7 @@
+
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Resources/Strings.resx
index a926fc42386a9b..4bf9b6d5b84a9a 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Resources/Strings.resx
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/Resources/Strings.resx
@@ -1,17 +1,17 @@
-
@@ -127,7 +127,7 @@
Cannot create instance of type '{0}' because one or more parameters cannot be bound to. Constructor parameters must have corresponding properties. Fields are not supported. Missing properties are: '{1}'
- Failed to convert configuration value at '{0}' to type '{1}'.
+ Failed to convert configuration value '{0}' at '{1}' to type '{2}'.
Failed to create instance of type '{0}'.
@@ -153,4 +153,4 @@
Cannot create instance of type '{0}' because multidimensional arrays are not supported.
-
\ No newline at end of file
+
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.Collections.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.Collections.cs
index 14b164b3f0acda..6ec4a5024eda98 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.Collections.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.Collections.cs
@@ -61,7 +61,7 @@ public void GetListNullValues()
var list = new List();
config.GetSection("StringList").Bind(list);
- Assert.Empty(list);
+ Assert.Equal([ null, null, null, null ], list);
}
[ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Ensure exception messages are in sync
@@ -2182,9 +2182,10 @@ public void CanBindInstantiatedIEnumerableWithNullItems()
var options = config.Get()!;
- Assert.Equal(2, options.InstantiatedIEnumerable.Count());
- Assert.Equal("Yo1", options.InstantiatedIEnumerable.ElementAt(0));
- Assert.Equal("Yo2", options.InstantiatedIEnumerable.ElementAt(1));
+ Assert.Equal(3, options.InstantiatedIEnumerable.Count());
+ Assert.Null(options.InstantiatedIEnumerable.ElementAt(0));
+ Assert.Equal("Yo1", options.InstantiatedIEnumerable.ElementAt(1));
+ Assert.Equal("Yo2", options.InstantiatedIEnumerable.ElementAt(2));
}
[Fact]
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 66d91cc0655304..a36c3b2d6e9e24 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
@@ -1147,5 +1147,36 @@ public enum MyValue
Value2,
Value3
}
+
+ public class NullConfiguration
+ {
+ public NullConfiguration()
+ {
+ // Initialize with non-default value to ensure binding will override these values
+ StringProperty1 = "Initial Value 1";
+ StringProperty2 = "Initial Value 2";
+ StringProperty3 = "Initial Value 3";
+
+ IntProperty1 = 123;
+ IntProperty2 = 456;
+ }
+ public string? StringProperty1 { get; set; }
+ public string? StringProperty2 { get; set; }
+ public string? StringProperty3 { get; set; }
+
+ public int? IntProperty1 { get; set; }
+ public int? IntProperty2 { get; set; }
+ }
+
+ public class ArraysContainer
+ {
+ public string[] StringArray1 { get; set; }
+ public string[] StringArray2 { get; set; }
+ public string[] StringArray3 { get; set; }
+
+ public byte[] ByteArray1 { get; set; }
+ public byte[] ByteArray2 { get; set; }
+ public byte[] ByteArray3 { 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 f949ea10dc87e5..a08ff66bd1e4d1 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs
@@ -6,8 +6,10 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
+using System.IO;
using System.Linq;
using System.Reflection;
+using System.Text;
#if BUILDING_SOURCE_GENERATOR_TESTS
using Microsoft.Extensions.Configuration;
#endif
@@ -208,14 +210,8 @@ public void EmptyStringIsNullable()
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
-#if BUILDING_SOURCE_GENERATOR_TESTS
- // Ensure exception messages are in sync
- Assert.Throws(() => config.GetValue("empty"));
- Assert.Throws(() => config.GetValue("empty"));
-#else
Assert.Null(config.GetValue("empty"));
Assert.Null(config.GetValue("empty"));
-#endif
}
[Fact]
@@ -326,8 +322,8 @@ public void CanBindToObjectProperty()
[Fact]
public void GetNullValue()
{
- #nullable enable
- #pragma warning disable IDE0004 // Cast is redundant
+#nullable enable
+#pragma warning disable IDE0004 // Cast is redundant
var dic = new Dictionary
{
@@ -377,8 +373,8 @@ public void GetNullValue()
Assert.Equal(0, config.GetSection("Nested:Integer").Get());
Assert.Null(config.GetSection("Object").Get());
- #pragma warning restore IDE0004
- #nullable restore
+#pragma warning restore IDE0004
+#nullable restore
}
[Fact]
@@ -619,13 +615,13 @@ public void ConsistentExceptionOnFailedBinding(Type type)
Assert.NotNull(exception.InnerException);
Assert.NotNull(getException.InnerException);
Assert.Equal(
- SR.Format(SR.Error_FailedBinding, ConfigKey, type),
+ SR.Format(SR.Error_FailedBinding, IncorrectValue, ConfigKey, type),
exception.Message);
Assert.Equal(
- SR.Format(SR.Error_FailedBinding, ConfigKey, type),
+ SR.Format(SR.Error_FailedBinding, IncorrectValue, ConfigKey, type),
getException.Message);
Assert.Equal(
- SR.Format(SR.Error_FailedBinding, ConfigKey, type),
+ SR.Format(SR.Error_FailedBinding, IncorrectValue, ConfigKey, type),
getValueException.Message);
}
@@ -649,7 +645,7 @@ public void ExceptionOnFailedBindingIncludesPath()
var exception = Assert.Throws(
() => config.Bind(options));
- Assert.Equal(SR.Format(SR.Error_FailedBinding, ConfigKey, typeof(int)),
+ Assert.Equal(SR.Format(SR.Error_FailedBinding, IncorrectValue, ConfigKey, typeof(int)),
exception.Message);
}
@@ -1793,10 +1789,9 @@ public void ExceptionWhenTryingToBindToByteArray()
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
- var exception = Assert.Throws(
- () => config.Get());
+ var exception = Assert.Throws(() => config.Get());
Assert.Equal(
- SR.Format(SR.Error_FailedBinding, "MyByteArray", typeof(byte[])),
+ SR.Format(SR.Error_FailedBinding, "(not a valid base64 string)", "MyByteArray", typeof(byte[])),
exception.Message);
}
@@ -2813,7 +2808,7 @@ public void CanGetEnumerableNotCollection()
Assert.Equal("John,Jane,Stephen", result.Names);
Assert.True(result.Enabled);
- Assert.Equal(new [] { "new", "class", "rosebud"}, result.Keywords);
+ Assert.Equal(new[] { "new", "class", "rosebud" }, result.Keywords);
}
[Fact]
@@ -2855,5 +2850,162 @@ public void EnsureThrowingWithCollectionAndErrorOnUnknownConfigurationOption()
internal class TestSettings { public Dictionary Values { get; init; } = []; }
#endif
+
+ [Fact]
+ public void BindWithNullValues()
+ {
+ //
+ // Try json provider first which used to replace the null configuration values with empty strings.
+ // Now it should be able to bind null values correctly and not replacing them.
+ //
+
+ string jsonConfig = @"
+ {
+ ""NullConfiguration"": {
+ ""StringProperty1"": ""New Value!"",
+ ""StringProperty2"": null,
+ ""StringProperty3"": """",
+ ""IntProperty1"": 42,
+ ""IntProperty2"": null,
+ },
+ }";
+
+ var configuration = new ConfigurationBuilder()
+ .AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(jsonConfig)))
+ .Build().GetSection("NullConfiguration");
+
+ NullConfiguration result = configuration.Get();
+
+ Assert.NotNull(result);
+ Assert.Equal("New Value!", result.StringProperty1);
+ Assert.Null(result.StringProperty2);
+ Assert.Null(result.IntProperty2);
+ Assert.Equal("", result.StringProperty3);
+ Assert.Equal(42, result.IntProperty1);
+
+ //
+ // Test with in-memory configuration provider which never replaced the null values with empty strings.
+ // But the binder used to treat the null values as non-existing values and not bind them at all.
+ //
+
+ var inMemoryConfiguration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary
+ {
+ { "NullConfiguration:StringProperty1", "New Value!" },
+ { "NullConfiguration:StringProperty2", null },
+ { "NullConfiguration:StringProperty3", "" },
+ { "NullConfiguration:IntProperty1", "42" },
+ { "NullConfiguration:IntProperty2", null }
+ })
+ .Build().GetSection("NullConfiguration");
+
+ NullConfiguration inMemoryResult = inMemoryConfiguration.Get();
+
+ Assert.NotNull(inMemoryResult);
+
+ Assert.Equal("New Value!", inMemoryResult.StringProperty1);
+
+ Assert.Null(inMemoryResult.StringProperty2);
+ Assert.Null(inMemoryResult.IntProperty2);
+ Assert.Equal("", inMemoryResult.StringProperty3);
+ Assert.Equal(42, inMemoryResult.IntProperty1);
+ }
+
+ [Fact]
+ public void BindArraysWithNullAndOtherValues()
+ {
+ // Arrays like other collection when binding, it will merge the existing values with the new ones we get from the configuration.
+ // Ensure null, empty, and other values work as expected.
+
+ string jsonConfig = @"
+ {
+ ""ArraysContainer"": {
+ ""StringArray1"": [""Value1"", ""Value2""],
+ ""StringArray2"": null,
+ ""StringArray3"": """", // should result empty array
+
+ // We can bind byte array values from base64 strings too. Let's cover this case too.
+ ""ByteArray1"": null,
+ ""ByteArray2"": """",
+ ""ByteArray3"": ""AAECAwQFBgcICQo="" // encode byte values [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ },
+ }";
+
+ var configuration = new ConfigurationBuilder()
+ .AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(jsonConfig)))
+ .Build().GetSection("ArraysContainer");
+
+ ArraysContainer instance = new(); // all properties are initialized to null.
+ configuration.Bind(instance);
+
+ Assert.NotNull(instance);
+ Assert.Equal(["Value1", "Value2"], instance.StringArray1);
+ Assert.Null(instance.StringArray2);
+
+ Assert.Empty(instance.StringArray3); // empty string should result in empty array
+ Assert.Empty(instance.ByteArray2); // empty string should result in empty array
+
+ Assert.Null(instance.ByteArray1);
+ Assert.Equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], instance.ByteArray3);
+
+ // Bind one more time and ensure the values are accumulated correctly.
+ configuration.Bind(instance);
+ Assert.Equal(["Value1", "Value2", "Value1", "Value2"], instance.StringArray1);
+ Assert.Null(instance.StringArray2);
+ Assert.Empty(instance.StringArray3); // empty string should result in empty array
+ Assert.Empty(instance.ByteArray2); // empty string should result in empty array
+
+ Assert.Null(instance.ByteArray1);
+#if BUILDING_SOURCE_GENERATOR_TESTS
+ // Source gen has different behavior with the byte array which should be addressed later
+ // Source gen override the existing array instead of merging the values.
+ Assert.Equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], instance.ByteArray3);
+#else
+ Assert.Equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], instance.ByteArray3);
+#endif
+
+ // Test the same accumulation behavior with in-memory configuration
+ var inMemoryConfiguration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary
+ {
+ // String arrays - use indexed keys for array elements
+ { "ArraysContainer:StringArray1:0", "Value1" },
+ { "ArraysContainer:StringArray1:1", "Value2" },
+ { "ArraysContainer:StringArray2", null },
+ { "ArraysContainer:StringArray3", "" },
+
+ // Byte arrays
+ { "ArraysContainer:ByteArray1", null },
+ { "ArraysContainer:ByteArray2", "" },
+ { "ArraysContainer:ByteArray3", "AAECAwQFBgcICQo=" } // encode byte values [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ })
+ .Build().GetSection("ArraysContainer");
+
+ ArraysContainer inMemoryInstance = new();
+ inMemoryConfiguration.Bind(inMemoryInstance);
+ Assert.Equal(["Value1", "Value2"], inMemoryInstance.StringArray1);
+ Assert.Null(inMemoryInstance.StringArray2);
+ Assert.Empty(inMemoryInstance.StringArray3); // empty string should result in empty array
+ Assert.Empty(inMemoryInstance.ByteArray2); // empty string should result in empty array
+
+ Assert.Null(inMemoryInstance.ByteArray1);
+ Assert.Equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], inMemoryInstance.ByteArray3);
+
+ // Bind one more time and ensure the values are accumulated correctly.
+ inMemoryConfiguration.Bind(inMemoryInstance);
+ Assert.Equal(["Value1", "Value2", "Value1", "Value2"], inMemoryInstance.StringArray1);
+ Assert.Null(inMemoryInstance.StringArray2);
+ Assert.Empty(inMemoryInstance.StringArray3); // empty string should result in empty array
+ Assert.Empty(inMemoryInstance.ByteArray2); // empty string should result in empty array
+
+ Assert.Null(inMemoryInstance.ByteArray1);
+#if BUILDING_SOURCE_GENERATOR_TESTS
+ // Source gen has different behavior with the byte array which should be addressed later
+ // Source gen override the existing array instead of merging the values.
+ Assert.Equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], instance.ByteArray3);
+#else
+ Assert.Equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], instance.ByteArray3);
+#endif
+ }
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind.generated.txt
index d969a2ce0a9d3d..3e57ba50f61842 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind.generated.txt
@@ -96,9 +96,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -107,7 +110,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -130,7 +133,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value0)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value0))
{
instance.MyString = value0;
}
@@ -143,45 +146,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ var value2 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value2) is IConfigurationSection section3)
{
- global::System.Collections.Generic.List? temp4 = instance.MyList;
- temp4 ??= new global::System.Collections.Generic.List();
- BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp4;
+ global::System.Collections.Generic.List? temp5 = instance.MyList;
+ temp5 ??= new global::System.Collections.Generic.List();
+ BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp5;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ var value6 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value6) is IConfigurationSection section7)
{
- global::System.Collections.Generic.Dictionary? temp7 = instance.MyDictionary;
- temp7 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp7;
+ global::System.Collections.Generic.Dictionary? temp9 = instance.MyDictionary;
+ temp9 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp9;
}
else
{
instance.MyDictionary = instance.MyDictionary;
}
- if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
+ var value10 = configuration.GetSection("MyComplexDictionary");
+ if (AsConfigWithChildren(value10) is IConfigurationSection section11)
{
- global::System.Collections.Generic.Dictionary? temp10 = instance.MyComplexDictionary;
- temp10 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyComplexDictionary = temp10;
+ global::System.Collections.Generic.Dictionary? temp13 = instance.MyComplexDictionary;
+ temp13 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
+ instance.MyComplexDictionary = temp13;
}
else
{
@@ -189,6 +198,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -247,7 +268,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Instance.generated.txt
index 243c0c75244179..ade1275493a46e 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Instance.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Instance.generated.txt
@@ -60,9 +60,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -71,7 +74,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -94,7 +97,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value0)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value0))
{
instance.MyString = value0;
}
@@ -107,45 +110,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ var value2 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value2) is IConfigurationSection section3)
{
- global::System.Collections.Generic.List? temp4 = instance.MyList;
- temp4 ??= new global::System.Collections.Generic.List();
- BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp4;
+ global::System.Collections.Generic.List? temp5 = instance.MyList;
+ temp5 ??= new global::System.Collections.Generic.List();
+ BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp5;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ var value6 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value6) is IConfigurationSection section7)
{
- global::System.Collections.Generic.Dictionary? temp7 = instance.MyDictionary;
- temp7 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp7;
+ global::System.Collections.Generic.Dictionary? temp9 = instance.MyDictionary;
+ temp9 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp9;
}
else
{
instance.MyDictionary = instance.MyDictionary;
}
- if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
+ var value10 = configuration.GetSection("MyComplexDictionary");
+ if (AsConfigWithChildren(value10) is IConfigurationSection section11)
{
- global::System.Collections.Generic.Dictionary? temp10 = instance.MyComplexDictionary;
- temp10 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyComplexDictionary = temp10;
+ global::System.Collections.Generic.Dictionary? temp13 = instance.MyComplexDictionary;
+ temp13 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
+ instance.MyComplexDictionary = temp13;
}
else
{
@@ -153,6 +162,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -193,7 +214,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Instance_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Instance_BinderOptions.generated.txt
index 52b29ac28967bb..71e72064b0b7b2 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Instance_BinderOptions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Instance_BinderOptions.generated.txt
@@ -60,9 +60,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -71,7 +74,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -94,7 +97,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value0)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value0))
{
instance.MyString = value0;
}
@@ -107,45 +110,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ var value2 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value2) is IConfigurationSection section3)
{
- global::System.Collections.Generic.List? temp4 = instance.MyList;
- temp4 ??= new global::System.Collections.Generic.List();
- BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp4;
+ global::System.Collections.Generic.List? temp5 = instance.MyList;
+ temp5 ??= new global::System.Collections.Generic.List();
+ BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp5;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ var value6 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value6) is IConfigurationSection section7)
{
- global::System.Collections.Generic.Dictionary? temp7 = instance.MyDictionary;
- temp7 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp7;
+ global::System.Collections.Generic.Dictionary? temp9 = instance.MyDictionary;
+ temp9 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp9;
}
else
{
instance.MyDictionary = instance.MyDictionary;
}
- if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
+ var value10 = configuration.GetSection("MyComplexDictionary");
+ if (AsConfigWithChildren(value10) is IConfigurationSection section11)
{
- global::System.Collections.Generic.Dictionary? temp10 = instance.MyComplexDictionary;
- temp10 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyComplexDictionary = temp10;
+ global::System.Collections.Generic.Dictionary? temp13 = instance.MyComplexDictionary;
+ temp13 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
+ instance.MyComplexDictionary = temp13;
}
else
{
@@ -153,6 +162,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -211,7 +232,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Key_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Key_Instance.generated.txt
index aa3c6a9e943107..2c0ef892a55e8d 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Key_Instance.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_Key_Instance.generated.txt
@@ -60,9 +60,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -71,7 +74,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -94,7 +97,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value0)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value0))
{
instance.MyString = value0;
}
@@ -107,45 +110,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ var value2 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value2) is IConfigurationSection section3)
{
- global::System.Collections.Generic.List? temp4 = instance.MyList;
- temp4 ??= new global::System.Collections.Generic.List();
- BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp4;
+ global::System.Collections.Generic.List? temp5 = instance.MyList;
+ temp5 ??= new global::System.Collections.Generic.List();
+ BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp5;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ var value6 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value6) is IConfigurationSection section7)
{
- global::System.Collections.Generic.Dictionary? temp7 = instance.MyDictionary;
- temp7 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp7;
+ global::System.Collections.Generic.Dictionary? temp9 = instance.MyDictionary;
+ temp9 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp9;
}
else
{
instance.MyDictionary = instance.MyDictionary;
}
- if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
+ var value10 = configuration.GetSection("MyComplexDictionary");
+ if (AsConfigWithChildren(value10) is IConfigurationSection section11)
{
- global::System.Collections.Generic.Dictionary? temp10 = instance.MyComplexDictionary;
- temp10 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyComplexDictionary = temp10;
+ global::System.Collections.Generic.Dictionary? temp13 = instance.MyComplexDictionary;
+ temp13 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
+ instance.MyComplexDictionary = temp13;
}
else
{
@@ -153,6 +162,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -193,7 +214,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_ParseTypeFromMethodParam.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_ParseTypeFromMethodParam.generated.txt
index 9f8f951bff2980..190b61098f5fd2 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_ParseTypeFromMethodParam.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Bind_ParseTypeFromMethodParam.generated.txt
@@ -54,6 +54,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
#endregion IConfiguration extensions.
#region Core binding extensions.
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
public static BinderOptions? GetBinderOptions(Action? configureOptions)
{
if (configureOptions is null)
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get.generated.txt
index 68ddfa4c042c13..6ea3124bfb01ee 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get.generated.txt
@@ -89,9 +89,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -102,9 +105,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- temp2.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ temp2.Add(ParseInt(value, section.Path));
+ }
}
}
@@ -117,7 +123,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -128,7 +134,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value3)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value3))
{
instance.MyString = value3;
}
@@ -141,45 +147,55 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value4 && !string.IsNullOrEmpty(value4))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value4))
{
- instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value4))
+ {
+ instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5)
+ var value5 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value5) is IConfigurationSection section6)
{
- global::System.Collections.Generic.List? temp7 = instance.MyList;
- temp7 ??= new global::System.Collections.Generic.List();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp7;
+ global::System.Collections.Generic.List? temp8 = instance.MyList;
+ temp8 ??= new global::System.Collections.Generic.List();
+ BindCore(section6, ref temp8, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp8;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section8)
+ var value9 = configuration.GetSection("MyArray");
+ if (AsConfigWithChildren(value9) is IConfigurationSection section10)
{
- int[]? temp10 = instance.MyArray;
- temp10 ??= new int[0];
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyArray = temp10;
+ int[]? temp12 = instance.MyArray;
+ temp12 ??= new int[0];
+ BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
+ instance.MyArray = temp12;
}
else
{
instance.MyArray = instance.MyArray;
}
+ if (instance.MyArray is null && TryGetConfigurationValue(value9, key: null, out string? value13) && value13 == string.Empty)
+ {
+ instance.MyArray = global::System.Array.Empty();
+ }
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11)
+ var value14 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value14) is IConfigurationSection section15)
{
- global::System.Collections.Generic.Dictionary? temp13 = instance.MyDictionary;
- temp13 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp13;
+ global::System.Collections.Generic.Dictionary? temp17 = instance.MyDictionary;
+ temp17 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section15, ref temp17, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp17;
}
else
{
@@ -191,9 +207,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
- if (configuration["MyInt"] is string value14 && !string.IsNullOrEmpty(value14))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value18))
{
- instance.MyInt = ParseInt(value14, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value18))
+ {
+ instance.MyInt = ParseInt(value18, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
@@ -201,6 +220,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -268,7 +299,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue.generated.txt
index 02e16bbfddd1de..ad3f7ab95c9472 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue.generated.txt
@@ -60,29 +60,39 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
- if (section.Value is not string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value) && !string.IsNullOrEmpty(value))
{
- return null;
+ if (type == typeof(int))
+ {
+ return ParseInt(value, section.Path);
+ }
+ else if (type == typeof(bool?))
+ {
+ return ParseBool(value, section.Path);
+ }
+ else if (type == typeof(byte[]))
+ {
+ return ParseByteArray(value, section.Path);
+ }
+ else if (type == typeof(global::System.Globalization.CultureInfo))
+ {
+ return ParseSystemGlobalizationCultureInfo(value, section.Path);
+ }
}
- if (type == typeof(int))
- {
- return ParseInt(value, section.Path);
- }
- else if (type == typeof(bool?))
- {
- return ParseBool(value, section.Path);
- }
- else if (type == typeof(byte[]))
- {
- return ParseByteArray(value, section.Path);
- }
- else if (type == typeof(global::System.Globalization.CultureInfo))
+ return null;
+ }
+
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
{
- return ParseSystemGlobalizationCultureInfo(value, section.Path);
+ return section.TryGetValue(key, out value);
}
-
- return null;
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
}
public static int ParseInt(string value, string? path)
@@ -93,7 +103,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
@@ -105,7 +115,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(bool)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(bool)}'.", exception);
}
}
@@ -117,7 +127,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(byte[])}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(byte[])}'.", exception);
}
}
@@ -129,7 +139,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(global::System.Globalization.CultureInfo)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(global::System.Globalization.CultureInfo)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_T_Key.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_T_Key.generated.txt
index 1c6888d831b7e2..cd9549063e3b8e 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_T_Key.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_T_Key.generated.txt
@@ -48,17 +48,27 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
- if (section.Value is not string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value) && !string.IsNullOrEmpty(value))
{
- return null;
+ if (type == typeof(int))
+ {
+ return ParseInt(value, section.Path);
+ }
}
- if (type == typeof(int))
+ return null;
+ }
+
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
{
- return ParseInt(value, section.Path);
+ return section.TryGetValue(key, out value);
}
-
- return null;
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
}
public static int ParseInt(string value, string? path)
@@ -69,7 +79,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_T_Key_DefaultValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_T_Key_DefaultValue.generated.txt
index 63cc9ff7ef3f14..d5830fd0832b44 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_T_Key_DefaultValue.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_T_Key_DefaultValue.generated.txt
@@ -48,17 +48,27 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
- if (section.Value is not string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value) && !string.IsNullOrEmpty(value))
{
- return null;
+ if (type == typeof(int))
+ {
+ return ParseInt(value, section.Path);
+ }
}
- if (type == typeof(int))
+ return null;
+ }
+
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
{
- return ParseInt(value, section.Path);
+ return section.TryGetValue(key, out value);
}
-
- return null;
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
}
public static int ParseInt(string value, string? path)
@@ -69,7 +79,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_TypeOf_Key.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_TypeOf_Key.generated.txt
index a0c635eca06b96..975269f002e347 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_TypeOf_Key.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_TypeOf_Key.generated.txt
@@ -48,17 +48,27 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
- if (section.Value is not string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value) && !string.IsNullOrEmpty(value))
{
- return null;
+ if (type == typeof(bool?))
+ {
+ return ParseBool(value, section.Path);
+ }
}
- if (type == typeof(bool?))
+ return null;
+ }
+
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
{
- return ParseBool(value, section.Path);
+ return section.TryGetValue(key, out value);
}
-
- return null;
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
}
public static bool ParseBool(string value, string? path)
@@ -69,7 +79,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(bool)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(bool)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_TypeOf_Key_DefaultValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_TypeOf_Key_DefaultValue.generated.txt
index 9a1655ddd5beeb..b3f7dd1eefb627 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_TypeOf_Key_DefaultValue.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/GetValue_TypeOf_Key_DefaultValue.generated.txt
@@ -48,17 +48,27 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
- if (section.Value is not string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value) && !string.IsNullOrEmpty(value))
{
- return null;
+ if (type == typeof(global::System.Globalization.CultureInfo))
+ {
+ return ParseSystemGlobalizationCultureInfo(value, section.Path);
+ }
}
- if (type == typeof(global::System.Globalization.CultureInfo))
+ return null;
+ }
+
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
{
- return ParseSystemGlobalizationCultureInfo(value, section.Path);
+ return section.TryGetValue(key, out value);
}
-
- return null;
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
}
public static global::System.Globalization.CultureInfo ParseSystemGlobalizationCultureInfo(string value, string? path)
@@ -69,7 +79,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(global::System.Globalization.CultureInfo)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(global::System.Globalization.CultureInfo)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_PrimitivesOnly.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_PrimitivesOnly.generated.txt
index 97cdab82512c59..2d4f4da184a350 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_PrimitivesOnly.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_PrimitivesOnly.generated.txt
@@ -71,9 +71,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
throw new InvalidOperationException();
}
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(configuration, key: null, out string? value))
{
- return ParseInt(value, section.Path);
+ if (!string.IsNullOrEmpty(value))
+ {
+ return ParseInt(value, section.Path);
+ }
}
}
else if (type == typeof(string))
@@ -82,7 +85,10 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
throw new InvalidOperationException();
}
- return section.Value;
+ if (TryGetConfigurationValue(configuration, key: null, out string? value))
+ {
+ return value;
+ }
}
else if (type == typeof(float))
{
@@ -90,9 +96,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
throw new InvalidOperationException();
}
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(configuration, key: null, out string? value))
{
- return ParseFloat(value, section.Path);
+ if (!string.IsNullOrEmpty(value))
+ {
+ return ParseFloat(value, section.Path);
+ }
}
}
else if (type == typeof(double))
@@ -101,15 +110,30 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
throw new InvalidOperationException();
}
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(configuration, key: null, out string? value))
{
- return ParseDouble(value, section.Path);
+ if (!string.IsNullOrEmpty(value))
+ {
+ return ParseDouble(value, section.Path);
+ }
}
}
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
public static bool HasValueOrChildren(IConfiguration configuration)
{
if ((configuration as IConfigurationSection)?.Value is not null)
@@ -154,7 +178,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
@@ -166,7 +190,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(float)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(float)}'.", exception);
}
}
@@ -178,7 +202,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(double)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(double)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_T.generated.txt
index 1c7b92598f41e8..9fbde7f573ef01 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_T.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_T.generated.txt
@@ -70,9 +70,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -83,9 +86,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- temp1.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ temp1.Add(ParseInt(value, section.Path));
+ }
}
}
@@ -98,7 +104,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -109,7 +115,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value2)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value2))
{
instance.MyString = value2;
}
@@ -122,45 +128,55 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value3 && !string.IsNullOrEmpty(value3))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value3))
{
- instance.MyInt = ParseInt(value3, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value3))
+ {
+ instance.MyInt = ParseInt(value3, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section4)
+ var value4 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value4) is IConfigurationSection section5)
{
- global::System.Collections.Generic.List? temp6 = instance.MyList;
- temp6 ??= new global::System.Collections.Generic.List();
- BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp6;
+ global::System.Collections.Generic.List? temp7 = instance.MyList;
+ temp7 ??= new global::System.Collections.Generic.List();
+ BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp7;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section7)
+ var value8 = configuration.GetSection("MyArray");
+ if (AsConfigWithChildren(value8) is IConfigurationSection section9)
{
- int[]? temp9 = instance.MyArray;
- temp9 ??= new int[0];
- BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
- instance.MyArray = temp9;
+ int[]? temp11 = instance.MyArray;
+ temp11 ??= new int[0];
+ BindCore(section9, ref temp11, defaultValueIfNotFound: false, binderOptions);
+ instance.MyArray = temp11;
}
else
{
instance.MyArray = instance.MyArray;
}
+ if (instance.MyArray is null && TryGetConfigurationValue(value8, key: null, out string? value12) && value12 == string.Empty)
+ {
+ instance.MyArray = global::System.Array.Empty();
+ }
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section10)
+ var value13 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value13) is IConfigurationSection section14)
{
- global::System.Collections.Generic.Dictionary? temp12 = instance.MyDictionary;
- temp12 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp12;
+ global::System.Collections.Generic.Dictionary? temp16 = instance.MyDictionary;
+ temp16 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section14, ref temp16, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp16;
}
else
{
@@ -168,6 +184,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -235,7 +263,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_T_BinderOptions.generated.txt
index 7598e236600076..4c5a9b5732038a 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_T_BinderOptions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_T_BinderOptions.generated.txt
@@ -70,9 +70,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -83,9 +86,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- temp1.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ temp1.Add(ParseInt(value, section.Path));
+ }
}
}
@@ -98,7 +104,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -109,7 +115,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value2)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value2))
{
instance.MyString = value2;
}
@@ -122,45 +128,55 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value3 && !string.IsNullOrEmpty(value3))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value3))
{
- instance.MyInt = ParseInt(value3, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value3))
+ {
+ instance.MyInt = ParseInt(value3, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section4)
+ var value4 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value4) is IConfigurationSection section5)
{
- global::System.Collections.Generic.List? temp6 = instance.MyList;
- temp6 ??= new global::System.Collections.Generic.List();
- BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp6;
+ global::System.Collections.Generic.List? temp7 = instance.MyList;
+ temp7 ??= new global::System.Collections.Generic.List();
+ BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp7;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section7)
+ var value8 = configuration.GetSection("MyArray");
+ if (AsConfigWithChildren(value8) is IConfigurationSection section9)
{
- int[]? temp9 = instance.MyArray;
- temp9 ??= new int[0];
- BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
- instance.MyArray = temp9;
+ int[]? temp11 = instance.MyArray;
+ temp11 ??= new int[0];
+ BindCore(section9, ref temp11, defaultValueIfNotFound: false, binderOptions);
+ instance.MyArray = temp11;
}
else
{
instance.MyArray = instance.MyArray;
}
+ if (instance.MyArray is null && TryGetConfigurationValue(value8, key: null, out string? value12) && value12 == string.Empty)
+ {
+ instance.MyArray = global::System.Array.Empty();
+ }
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section10)
+ var value13 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value13) is IConfigurationSection section14)
{
- global::System.Collections.Generic.Dictionary? temp12 = instance.MyDictionary;
- temp12 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp12;
+ global::System.Collections.Generic.Dictionary? temp16 = instance.MyDictionary;
+ temp16 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section14, ref temp16, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp16;
}
else
{
@@ -168,6 +184,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -235,7 +263,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_TypeOf.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_TypeOf.generated.txt
index 589f4b4fb10d93..335fd179bc58dc 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_TypeOf.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_TypeOf.generated.txt
@@ -70,9 +70,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
@@ -80,6 +83,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -147,7 +162,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_TypeOf_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_TypeOf_BinderOptions.generated.txt
index 53ee49e5ed1c69..95eec86a28cb63 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_TypeOf_BinderOptions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ConfigurationBinder/Version1/Get_TypeOf_BinderOptions.generated.txt
@@ -70,9 +70,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
@@ -80,6 +83,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -147,7 +162,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/BindConfiguration.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/BindConfiguration.generated.txt
index 7981bfc8de6d56..30a05d9f4593fa 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/BindConfiguration.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/BindConfiguration.generated.txt
@@ -101,9 +101,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -112,7 +115,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value1)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value1))
{
instance.MyString = value1;
}
@@ -125,21 +128,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value2 && !string.IsNullOrEmpty(value2))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value2))
{
- instance.MyInt = ParseInt(value2, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value2))
+ {
+ instance.MyInt = ParseInt(value2, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3)
+ var value3 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value3) is IConfigurationSection section4)
{
- global::System.Collections.Generic.List? temp5 = instance.MyList;
- temp5 ??= new global::System.Collections.Generic.List();
- BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp5;
+ global::System.Collections.Generic.List? temp6 = instance.MyList;
+ temp6 ??= new global::System.Collections.Generic.List();
+ BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp6;
}
else
{
@@ -147,6 +154,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -214,7 +233,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/BindConfigurationWithConfigureActions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/BindConfigurationWithConfigureActions.generated.txt
index 7c89f54e229024..8fd2e401d38d13 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/BindConfigurationWithConfigureActions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/BindConfigurationWithConfigureActions.generated.txt
@@ -101,9 +101,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -112,7 +115,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value1)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value1))
{
instance.MyString = value1;
}
@@ -125,21 +128,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value2 && !string.IsNullOrEmpty(value2))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value2))
{
- instance.MyInt = ParseInt(value2, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value2))
+ {
+ instance.MyInt = ParseInt(value2, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3)
+ var value3 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value3) is IConfigurationSection section4)
{
- global::System.Collections.Generic.List? temp5 = instance.MyList;
- temp5 ??= new global::System.Collections.Generic.List();
- BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp5;
+ global::System.Collections.Generic.List? temp6 = instance.MyList;
+ temp6 ??= new global::System.Collections.Generic.List();
+ BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp6;
}
else
{
@@ -147,6 +154,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -214,7 +233,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/Bind_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/Bind_T.generated.txt
index d76fb22d50b1ab..f6a67be1dcd641 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/Bind_T.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/Bind_T.generated.txt
@@ -107,9 +107,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -118,7 +121,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value1)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value1))
{
instance.MyString = value1;
}
@@ -131,21 +134,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value2 && !string.IsNullOrEmpty(value2))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value2))
{
- instance.MyInt = ParseInt(value2, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value2))
+ {
+ instance.MyInt = ParseInt(value2, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3)
+ var value3 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value3) is IConfigurationSection section4)
{
- global::System.Collections.Generic.List? temp5 = instance.MyList;
- temp5 ??= new global::System.Collections.Generic.List();
- BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp5;
+ global::System.Collections.Generic.List? temp6 = instance.MyList;
+ temp6 ??= new global::System.Collections.Generic.List();
+ BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp6;
}
else
{
@@ -153,6 +160,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -220,7 +239,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/Bind_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/Bind_T_BinderOptions.generated.txt
index 782feb787862fa..f7716c28640037 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/Bind_T_BinderOptions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/OptionsBuilder/Version1/Bind_T_BinderOptions.generated.txt
@@ -101,9 +101,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -112,7 +115,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value1)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value1))
{
instance.MyString = value1;
}
@@ -125,21 +128,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value2 && !string.IsNullOrEmpty(value2))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value2))
{
- instance.MyInt = ParseInt(value2, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value2))
+ {
+ instance.MyInt = ParseInt(value2, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3)
+ var value3 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value3) is IConfigurationSection section4)
{
- global::System.Collections.Generic.List? temp5 = instance.MyList;
- temp5 ??= new global::System.Collections.Generic.List();
- BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp5;
+ global::System.Collections.Generic.List? temp6 = instance.MyList;
+ temp6 ??= new global::System.Collections.Generic.List();
+ BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp6;
}
else
{
@@ -147,6 +154,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -214,7 +233,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T.generated.txt
index a15b599220ada1..ed219528caa918 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T.generated.txt
@@ -95,9 +95,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -106,9 +109,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
@@ -134,7 +140,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -145,7 +151,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value3)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value3))
{
instance.MyString = value3;
}
@@ -158,45 +164,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value4 && !string.IsNullOrEmpty(value4))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value4))
{
- instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value4))
+ {
+ instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5)
+ var value5 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value5) is IConfigurationSection section6)
{
- global::System.Collections.Generic.List? temp7 = instance.MyList;
- temp7 ??= new global::System.Collections.Generic.List();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp7;
+ global::System.Collections.Generic.List? temp8 = instance.MyList;
+ temp8 ??= new global::System.Collections.Generic.List();
+ BindCore(section6, ref temp8, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp8;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8)
+ var value9 = configuration.GetSection("MyList2");
+ if (AsConfigWithChildren(value9) is IConfigurationSection section10)
{
- global::System.Collections.Generic.List? temp10 = instance.MyList2;
- temp10 ??= new global::System.Collections.Generic.List();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyList2 = temp10;
+ global::System.Collections.Generic.List? temp12 = instance.MyList2;
+ temp12 ??= new global::System.Collections.Generic.List();
+ BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList2 = temp12;
}
else
{
instance.MyList2 = instance.MyList2;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11)
+ var value13 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value13) is IConfigurationSection section14)
{
- global::System.Collections.Generic.Dictionary? temp13 = instance.MyDictionary;
- temp13 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp13;
+ global::System.Collections.Generic.Dictionary? temp16 = instance.MyDictionary;
+ temp16 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section14, ref temp16, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp16;
}
else
{
@@ -204,6 +216,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -271,7 +295,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_BinderOptions.generated.txt
index 3e65d96e3f47bb..e2f4bd350bad5f 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_BinderOptions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_BinderOptions.generated.txt
@@ -95,9 +95,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -106,9 +109,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
@@ -134,7 +140,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -145,7 +151,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value3)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value3))
{
instance.MyString = value3;
}
@@ -158,45 +164,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value4 && !string.IsNullOrEmpty(value4))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value4))
{
- instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value4))
+ {
+ instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5)
+ var value5 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value5) is IConfigurationSection section6)
{
- global::System.Collections.Generic.List? temp7 = instance.MyList;
- temp7 ??= new global::System.Collections.Generic.List();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp7;
+ global::System.Collections.Generic.List? temp8 = instance.MyList;
+ temp8 ??= new global::System.Collections.Generic.List();
+ BindCore(section6, ref temp8, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp8;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8)
+ var value9 = configuration.GetSection("MyList2");
+ if (AsConfigWithChildren(value9) is IConfigurationSection section10)
{
- global::System.Collections.Generic.List? temp10 = instance.MyList2;
- temp10 ??= new global::System.Collections.Generic.List();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyList2 = temp10;
+ global::System.Collections.Generic.List? temp12 = instance.MyList2;
+ temp12 ??= new global::System.Collections.Generic.List();
+ BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList2 = temp12;
}
else
{
instance.MyList2 = instance.MyList2;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11)
+ var value13 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value13) is IConfigurationSection section14)
{
- global::System.Collections.Generic.Dictionary? temp13 = instance.MyDictionary;
- temp13 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp13;
+ global::System.Collections.Generic.Dictionary? temp16 = instance.MyDictionary;
+ temp16 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section14, ref temp16, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp16;
}
else
{
@@ -204,6 +216,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -271,7 +295,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_name.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_name.generated.txt
index af7456240056c0..6490fc6d90eceb 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_name.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_name.generated.txt
@@ -95,9 +95,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -106,9 +109,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
@@ -134,7 +140,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -145,7 +151,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value3)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value3))
{
instance.MyString = value3;
}
@@ -158,45 +164,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value4 && !string.IsNullOrEmpty(value4))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value4))
{
- instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value4))
+ {
+ instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5)
+ var value5 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value5) is IConfigurationSection section6)
{
- global::System.Collections.Generic.List? temp7 = instance.MyList;
- temp7 ??= new global::System.Collections.Generic.List();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp7;
+ global::System.Collections.Generic.List? temp8 = instance.MyList;
+ temp8 ??= new global::System.Collections.Generic.List();
+ BindCore(section6, ref temp8, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp8;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8)
+ var value9 = configuration.GetSection("MyList2");
+ if (AsConfigWithChildren(value9) is IConfigurationSection section10)
{
- global::System.Collections.Generic.List? temp10 = instance.MyList2;
- temp10 ??= new global::System.Collections.Generic.List();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyList2 = temp10;
+ global::System.Collections.Generic.List? temp12 = instance.MyList2;
+ temp12 ??= new global::System.Collections.Generic.List();
+ BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList2 = temp12;
}
else
{
instance.MyList2 = instance.MyList2;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11)
+ var value13 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value13) is IConfigurationSection section14)
{
- global::System.Collections.Generic.Dictionary? temp13 = instance.MyDictionary;
- temp13 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp13;
+ global::System.Collections.Generic.Dictionary? temp16 = instance.MyDictionary;
+ temp16 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section14, ref temp16, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp16;
}
else
{
@@ -204,6 +216,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -271,7 +295,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_name_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_name_BinderOptions.generated.txt
index b5f3bd2069baa4..d3abf7baf31cb4 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_name_BinderOptions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Version1/Configure_T_name_BinderOptions.generated.txt
@@ -89,9 +89,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -100,9 +103,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
@@ -128,7 +134,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -139,7 +145,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value3)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value3))
{
instance.MyString = value3;
}
@@ -152,45 +158,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value4 && !string.IsNullOrEmpty(value4))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value4))
{
- instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value4))
+ {
+ instance.MyInt = ParseInt(value4, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5)
+ var value5 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value5) is IConfigurationSection section6)
{
- global::System.Collections.Generic.List? temp7 = instance.MyList;
- temp7 ??= new global::System.Collections.Generic.List();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp7;
+ global::System.Collections.Generic.List? temp8 = instance.MyList;
+ temp8 ??= new global::System.Collections.Generic.List();
+ BindCore(section6, ref temp8, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp8;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8)
+ var value9 = configuration.GetSection("MyList2");
+ if (AsConfigWithChildren(value9) is IConfigurationSection section10)
{
- global::System.Collections.Generic.List? temp10 = instance.MyList2;
- temp10 ??= new global::System.Collections.Generic.List();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyList2 = temp10;
+ global::System.Collections.Generic.List? temp12 = instance.MyList2;
+ temp12 ??= new global::System.Collections.Generic.List();
+ BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList2 = temp12;
}
else
{
instance.MyList2 = instance.MyList2;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11)
+ var value13 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value13) is IConfigurationSection section14)
{
- global::System.Collections.Generic.Dictionary? temp13 = instance.MyDictionary;
- temp13 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp13;
+ global::System.Collections.Generic.Dictionary? temp16 = instance.MyDictionary;
+ temp16 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section14, ref temp16, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp16;
}
else
{
@@ -198,6 +210,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -265,7 +289,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/Collections.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/Collections.generated.txt
index 3bb04809ead380..d97aa128e8d688 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/Collections.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/Collections.generated.txt
@@ -71,9 +71,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance[section.Key] = ParseInt(value, section.Path);
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance[section.Key] = ParseInt(value, section.Path);
+ }
}
}
}
@@ -82,7 +85,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance.Add(value);
}
@@ -98,9 +101,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- temp.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ temp.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -114,9 +120,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- temp[section.Key] = ParseInt(value, section.Path);
+ if (!string.IsNullOrEmpty(value))
+ {
+ temp[section.Key] = ParseInt(value, section.Path);
+ }
}
}
}
@@ -127,7 +136,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
temp.Add(value);
}
@@ -138,61 +147,66 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClassWithCustomCollections), s_configKeys_ProgramMyClassWithCustomCollections, configuration, binderOptions);
- if (AsConfigWithChildren(configuration.GetSection("CustomDictionary")) is IConfigurationSection section1)
+ var value1 = configuration.GetSection("CustomDictionary");
+ if (AsConfigWithChildren(value1) is IConfigurationSection section2)
{
- global::Program.CustomDictionary? temp3 = instance.CustomDictionary;
- temp3 ??= new global::Program.CustomDictionary();
- BindCore(section1, ref temp3, defaultValueIfNotFound: false, binderOptions);
- instance.CustomDictionary = temp3;
+ global::Program.CustomDictionary? temp4 = instance.CustomDictionary;
+ temp4 ??= new global::Program.CustomDictionary();
+ BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions);
+ instance.CustomDictionary = temp4;
}
else
{
instance.CustomDictionary = instance.CustomDictionary;
}
- if (AsConfigWithChildren(configuration.GetSection("CustomList")) is IConfigurationSection section4)
+ var value5 = configuration.GetSection("CustomList");
+ if (AsConfigWithChildren(value5) is IConfigurationSection section6)
{
- global::Program.CustomList? temp6 = instance.CustomList;
- temp6 ??= new global::Program.CustomList();
- BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions);
- instance.CustomList = temp6;
+ global::Program.CustomList? temp8 = instance.CustomList;
+ temp8 ??= new global::Program.CustomList();
+ BindCore(section6, ref temp8, defaultValueIfNotFound: false, binderOptions);
+ instance.CustomList = temp8;
}
else
{
instance.CustomList = instance.CustomList;
}
- if (AsConfigWithChildren(configuration.GetSection("IReadOnlyList")) is IConfigurationSection section7)
+ var value9 = configuration.GetSection("IReadOnlyList");
+ if (AsConfigWithChildren(value9) is IConfigurationSection section10)
{
- global::System.Collections.Generic.IReadOnlyList? temp9 = instance.IReadOnlyList;
- temp9 = temp9 is null ? (global::System.Collections.Generic.IReadOnlyList)new List() : (global::System.Collections.Generic.IReadOnlyList)new List(temp9);
- BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
- instance.IReadOnlyList = temp9;
+ global::System.Collections.Generic.IReadOnlyList? temp12 = instance.IReadOnlyList;
+ temp12 = temp12 is null ? (global::System.Collections.Generic.IReadOnlyList)new List() : (global::System.Collections.Generic.IReadOnlyList)new List(temp12);
+ BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
+ instance.IReadOnlyList = temp12;
}
else
{
instance.IReadOnlyList = instance.IReadOnlyList;
}
- if (AsConfigWithChildren(configuration.GetSection("IReadOnlyDictionary")) is IConfigurationSection section10)
+ var value13 = configuration.GetSection("IReadOnlyDictionary");
+ if (AsConfigWithChildren(value13) is IConfigurationSection section14)
{
- global::System.Collections.Generic.IReadOnlyDictionary? temp12 = instance.IReadOnlyDictionary;
- temp12 = temp12 is null ? (global::System.Collections.Generic.IReadOnlyDictionary)new Dictionary() : (global::System.Collections.Generic.IReadOnlyDictionary)temp12.ToDictionary(pair => pair.Key, pair => pair.Value);
- BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
- instance.IReadOnlyDictionary = temp12;
+ global::System.Collections.Generic.IReadOnlyDictionary? temp16 = instance.IReadOnlyDictionary;
+ temp16 = temp16 is null ? (global::System.Collections.Generic.IReadOnlyDictionary)new Dictionary() : (global::System.Collections.Generic.IReadOnlyDictionary)temp16.ToDictionary(pair => pair.Key, pair => pair.Value);
+ BindCore(section14, ref temp16, defaultValueIfNotFound: false, binderOptions);
+ instance.IReadOnlyDictionary = temp16;
}
else
{
instance.IReadOnlyDictionary = instance.IReadOnlyDictionary;
}
- if (AsConfigWithChildren(configuration.GetSection("CollectionStructExplicit")) is IConfigurationSection section13)
+ var value17 = configuration.GetSection("CollectionStructExplicit");
+ if (AsConfigWithChildren(value17) is IConfigurationSection section18)
{
- global::Program.CollectionStructExplicit temp14 = instance.CollectionStructExplicit;
- var temp15 = new global::Program.CollectionStructExplicit();
- BindCore(section13, ref temp15, defaultValueIfNotFound: false, binderOptions);
- instance.CollectionStructExplicit = temp15;
- temp14 = temp15;
+ global::Program.CollectionStructExplicit temp19 = instance.CollectionStructExplicit;
+ var temp20 = new global::Program.CollectionStructExplicit();
+ BindCore(section18, ref temp20, defaultValueIfNotFound: false, binderOptions);
+ instance.CollectionStructExplicit = temp20;
+ temp19 = temp20;
}
else
{
@@ -200,6 +214,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -267,7 +293,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/DefaultConstructorParameters.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/DefaultConstructorParameters.generated.txt
index bf84547cab0615..285b5f782a58bb 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/DefaultConstructorParameters.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/DefaultConstructorParameters.generated.txt
@@ -64,92 +64,182 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static global::Program.ClassWhereParametersHaveDefaultValue InitializeProgramClassWhereParametersHaveDefaultValue(IConfiguration configuration, BinderOptions? binderOptions)
{
string name = "John Doe"!;
- if (configuration["Name"] is string value0)
+ if (TryGetConfigurationValue(configuration, key: "Name", out string? value0))
{
name = value0;
}
string address = "1 Microsoft Way"!;
- if (configuration["Address"] is string value1)
+ if (TryGetConfigurationValue(configuration, key: "Address", out string? value1))
{
address = value1;
}
int age = (int)(42);
- if (configuration["Age"] is string value2 && !string.IsNullOrEmpty(value2))
+ if (TryGetConfigurationValue(configuration, key: "Age", out string? value2))
{
- age = ParseInt(value2, configuration.GetSection("Age").Path);
+ if (!string.IsNullOrEmpty(value2))
+ {
+ age = ParseInt(value2, configuration.GetSection("Age").Path);
+ }
}
float f = 42F;
- if (configuration["F"] is string value3 && !string.IsNullOrEmpty(value3))
+ if (TryGetConfigurationValue(configuration, key: "F", out string? value3))
{
- f = ParseFloat(value3, configuration.GetSection("F").Path);
+ if (!string.IsNullOrEmpty(value3))
+ {
+ f = ParseFloat(value3, configuration.GetSection("F").Path);
+ }
}
double d = 3.1415899999999999D;
- if (configuration["D"] is string value4 && !string.IsNullOrEmpty(value4))
+ if (TryGetConfigurationValue(configuration, key: "D", out string? value4))
{
- d = ParseDouble(value4, configuration.GetSection("D").Path);
+ if (!string.IsNullOrEmpty(value4))
+ {
+ d = ParseDouble(value4, configuration.GetSection("D").Path);
+ }
}
decimal m = 3.1415926535897932384626433M;
- if (configuration["M"] is string value5 && !string.IsNullOrEmpty(value5))
+ if (TryGetConfigurationValue(configuration, key: "M", out string? value5))
{
- m = ParseDecimal(value5, configuration.GetSection("M").Path);
+ if (!string.IsNullOrEmpty(value5))
+ {
+ m = ParseDecimal(value5, configuration.GetSection("M").Path);
+ }
}
global::System.StringComparison sc = (global::System.StringComparison)(4);
- if (configuration["SC"] is string value6 && !string.IsNullOrEmpty(value6))
+ if (TryGetConfigurationValue(configuration, key: "SC", out string? value6))
{
- sc = ParseEnum(value6, configuration.GetSection("SC").Path);
+ if (!string.IsNullOrEmpty(value6))
+ {
+ sc = ParseEnum(value6, configuration.GetSection("SC").Path);
+ }
}
char c = 'q';
- if (configuration["C"] is string value7 && !string.IsNullOrEmpty(value7))
+ if (TryGetConfigurationValue(configuration, key: "C", out string? value7))
{
- c = ParseChar(value7, configuration.GetSection("C").Path);
+ if (!string.IsNullOrEmpty(value7))
+ {
+ c = ParseChar(value7, configuration.GetSection("C").Path);
+ }
}
int? nage = (int?)(42);
- if (configuration["NAge"] is string value8 && !string.IsNullOrEmpty(value8))
+ if (TryGetConfigurationValue(configuration, key: "NAge", out string? value8))
{
- nage = ParseInt(value8, configuration.GetSection("NAge").Path);
+ if (value8 is null)
+ {
+ nage = null;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(value8))
+ {
+ nage = ParseInt(value8, configuration.GetSection("NAge").Path);
+ }
+ }
}
float? nf = 42F;
- if (configuration["NF"] is string value9 && !string.IsNullOrEmpty(value9))
+ if (TryGetConfigurationValue(configuration, key: "NF", out string? value9))
{
- nf = ParseFloat(value9, configuration.GetSection("NF").Path);
+ if (value9 is null)
+ {
+ nf = null;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(value9))
+ {
+ nf = ParseFloat(value9, configuration.GetSection("NF").Path);
+ }
+ }
}
double? nd = 3.1415899999999999D;
- if (configuration["ND"] is string value10 && !string.IsNullOrEmpty(value10))
+ if (TryGetConfigurationValue(configuration, key: "ND", out string? value10))
{
- nd = ParseDouble(value10, configuration.GetSection("ND").Path);
+ if (value10 is null)
+ {
+ nd = null;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(value10))
+ {
+ nd = ParseDouble(value10, configuration.GetSection("ND").Path);
+ }
+ }
}
decimal? nm = 3.1415926535897932384626433M;
- if (configuration["NM"] is string value11 && !string.IsNullOrEmpty(value11))
+ if (TryGetConfigurationValue(configuration, key: "NM", out string? value11))
{
- nm = ParseDecimal(value11, configuration.GetSection("NM").Path);
+ if (value11 is null)
+ {
+ nm = null;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(value11))
+ {
+ nm = ParseDecimal(value11, configuration.GetSection("NM").Path);
+ }
+ }
}
global::System.StringComparison? nsc = (global::System.StringComparison?)(4);
- if (configuration["NSC"] is string value12 && !string.IsNullOrEmpty(value12))
+ if (TryGetConfigurationValue(configuration, key: "NSC", out string? value12))
{
- nsc = ParseEnum(value12, configuration.GetSection("NSC").Path);
+ if (value12 is null)
+ {
+ nsc = null;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(value12))
+ {
+ nsc = ParseEnum(value12, configuration.GetSection("NSC").Path);
+ }
+ }
}
char? nc = 'q';
- if (configuration["NC"] is string value13 && !string.IsNullOrEmpty(value13))
+ if (TryGetConfigurationValue(configuration, key: "NC", out string? value13))
{
- nc = ParseChar(value13, configuration.GetSection("NC").Path);
+ if (value13 is null)
+ {
+ nc = null;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(value13))
+ {
+ nc = ParseChar(value13, configuration.GetSection("NC").Path);
+ }
+ }
}
return new global::Program.ClassWhereParametersHaveDefaultValue(name, address, age, f, d, m, sc, c, nage, nf, nd, nm, nsc, nc);
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -181,7 +271,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(T)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(T)}'.", exception);
}
}
@@ -193,7 +283,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
@@ -205,7 +295,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(float)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(float)}'.", exception);
}
}
@@ -217,7 +307,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(double)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(double)}'.", exception);
}
}
@@ -229,7 +319,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(decimal)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(decimal)}'.", exception);
}
}
@@ -241,7 +331,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(char)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(char)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/EmptyConfigType.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/EmptyConfigType.generated.txt
index b9ca5b5e42e500..56e3b5dffb1b21 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/EmptyConfigType.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/EmptyConfigType.generated.txt
@@ -67,12 +67,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::TypeWithNoMembers_Wrapper), s_configKeys_TypeWithNoMembers_Wrapper, configuration, binderOptions);
- if (AsConfigWithChildren(configuration.GetSection("Member")) is IConfigurationSection section0)
+ var value0 = configuration.GetSection("Member");
+ if (AsConfigWithChildren(value0) is IConfigurationSection section1)
{
instance.Member ??= new global::TypeWithNoMembers();
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/Primitives.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/Primitives.generated.txt
index 862e062c054995..506c557f1a1840 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/Primitives.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/Primitives.generated.txt
@@ -60,52 +60,67 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["Prop0"] is string value0 && !string.IsNullOrEmpty(value0))
+ if (TryGetConfigurationValue(configuration, key: "Prop0", out string? value0))
{
- instance.Prop0 = ParseBool(value0, configuration.GetSection("Prop0").Path);
+ if (!string.IsNullOrEmpty(value0))
+ {
+ instance.Prop0 = ParseBool(value0, configuration.GetSection("Prop0").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop0 = instance.Prop0;
}
- if (configuration["Prop1"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "Prop1", out string? value1))
{
- instance.Prop1 = ParseByte(value1, configuration.GetSection("Prop1").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.Prop1 = ParseByte(value1, configuration.GetSection("Prop1").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop1 = instance.Prop1;
}
- if (configuration["Prop2"] is string value2 && !string.IsNullOrEmpty(value2))
+ if (TryGetConfigurationValue(configuration, key: "Prop2", out string? value2))
{
- instance.Prop2 = ParseSbyte(value2, configuration.GetSection("Prop2").Path);
+ if (!string.IsNullOrEmpty(value2))
+ {
+ instance.Prop2 = ParseSbyte(value2, configuration.GetSection("Prop2").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop2 = instance.Prop2;
}
- if (configuration["Prop3"] is string value3 && !string.IsNullOrEmpty(value3))
+ if (TryGetConfigurationValue(configuration, key: "Prop3", out string? value3))
{
- instance.Prop3 = ParseChar(value3, configuration.GetSection("Prop3").Path);
+ if (!string.IsNullOrEmpty(value3))
+ {
+ instance.Prop3 = ParseChar(value3, configuration.GetSection("Prop3").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop3 = instance.Prop3;
}
- if (configuration["Prop4"] is string value4 && !string.IsNullOrEmpty(value4))
+ if (TryGetConfigurationValue(configuration, key: "Prop4", out string? value4))
{
- instance.Prop4 = ParseDouble(value4, configuration.GetSection("Prop4").Path);
+ if (!string.IsNullOrEmpty(value4))
+ {
+ instance.Prop4 = ParseDouble(value4, configuration.GetSection("Prop4").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop4 = instance.Prop4;
}
- if (configuration["Prop5"] is string value5)
+ if (TryGetConfigurationValue(configuration, key: "Prop5", out string? value5))
{
instance.Prop5 = value5;
}
@@ -118,70 +133,91 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["Prop6"] is string value6 && !string.IsNullOrEmpty(value6))
+ if (TryGetConfigurationValue(configuration, key: "Prop6", out string? value6))
{
- instance.Prop6 = ParseInt(value6, configuration.GetSection("Prop6").Path);
+ if (!string.IsNullOrEmpty(value6))
+ {
+ instance.Prop6 = ParseInt(value6, configuration.GetSection("Prop6").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop6 = instance.Prop6;
}
- if (configuration["Prop8"] is string value7 && !string.IsNullOrEmpty(value7))
+ if (TryGetConfigurationValue(configuration, key: "Prop8", out string? value7))
{
- instance.Prop8 = ParseShort(value7, configuration.GetSection("Prop8").Path);
+ if (!string.IsNullOrEmpty(value7))
+ {
+ instance.Prop8 = ParseShort(value7, configuration.GetSection("Prop8").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop8 = instance.Prop8;
}
- if (configuration["Prop9"] is string value8 && !string.IsNullOrEmpty(value8))
+ if (TryGetConfigurationValue(configuration, key: "Prop9", out string? value8))
{
- instance.Prop9 = ParseLong(value8, configuration.GetSection("Prop9").Path);
+ if (!string.IsNullOrEmpty(value8))
+ {
+ instance.Prop9 = ParseLong(value8, configuration.GetSection("Prop9").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop9 = instance.Prop9;
}
- if (configuration["Prop10"] is string value9 && !string.IsNullOrEmpty(value9))
+ if (TryGetConfigurationValue(configuration, key: "Prop10", out string? value9))
{
- instance.Prop10 = ParseFloat(value9, configuration.GetSection("Prop10").Path);
+ if (!string.IsNullOrEmpty(value9))
+ {
+ instance.Prop10 = ParseFloat(value9, configuration.GetSection("Prop10").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop10 = instance.Prop10;
}
- if (configuration["Prop13"] is string value10 && !string.IsNullOrEmpty(value10))
+ if (TryGetConfigurationValue(configuration, key: "Prop13", out string? value10))
{
- instance.Prop13 = ParseUshort(value10, configuration.GetSection("Prop13").Path);
+ if (!string.IsNullOrEmpty(value10))
+ {
+ instance.Prop13 = ParseUshort(value10, configuration.GetSection("Prop13").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop13 = instance.Prop13;
}
- if (configuration["Prop14"] is string value11 && !string.IsNullOrEmpty(value11))
+ if (TryGetConfigurationValue(configuration, key: "Prop14", out string? value11))
{
- instance.Prop14 = ParseUint(value11, configuration.GetSection("Prop14").Path);
+ if (!string.IsNullOrEmpty(value11))
+ {
+ instance.Prop14 = ParseUint(value11, configuration.GetSection("Prop14").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop14 = instance.Prop14;
}
- if (configuration["Prop15"] is string value12 && !string.IsNullOrEmpty(value12))
+ if (TryGetConfigurationValue(configuration, key: "Prop15", out string? value12))
{
- instance.Prop15 = ParseUlong(value12, configuration.GetSection("Prop15").Path);
+ if (!string.IsNullOrEmpty(value12))
+ {
+ instance.Prop15 = ParseUlong(value12, configuration.GetSection("Prop15").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop15 = instance.Prop15;
}
- if (configuration["Prop16"] is string value13)
+ if (TryGetConfigurationValue(configuration, key: "Prop16", out string? value13))
{
instance.Prop16 = value13;
}
@@ -194,9 +230,19 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["Prop17"] is string value14 && !string.IsNullOrEmpty(value14))
+ if (TryGetConfigurationValue(configuration, key: "Prop17", out string? value14))
{
- instance.Prop17 = ParseSystemGlobalizationCultureInfo(value14, configuration.GetSection("Prop17").Path);
+ if (value14 is null)
+ {
+ instance.Prop17 = null;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(value14))
+ {
+ instance.Prop17 = ParseSystemGlobalizationCultureInfo(value14, configuration.GetSection("Prop17").Path);
+ }
+ }
}
else if (defaultValueIfNotFound)
{
@@ -207,54 +253,79 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["Prop19"] is string value15 && !string.IsNullOrEmpty(value15))
+ if (TryGetConfigurationValue(configuration, key: "Prop19", out string? value15))
{
- instance.Prop19 = ParseSystemDateTime(value15, configuration.GetSection("Prop19").Path);
+ if (!string.IsNullOrEmpty(value15))
+ {
+ instance.Prop19 = ParseSystemDateTime(value15, configuration.GetSection("Prop19").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop19 = instance.Prop19;
}
- if (configuration["Prop20"] is string value16 && !string.IsNullOrEmpty(value16))
+ if (TryGetConfigurationValue(configuration, key: "Prop20", out string? value16))
{
- instance.Prop20 = ParseSystemDateTimeOffset(value16, configuration.GetSection("Prop20").Path);
+ if (!string.IsNullOrEmpty(value16))
+ {
+ instance.Prop20 = ParseSystemDateTimeOffset(value16, configuration.GetSection("Prop20").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop20 = instance.Prop20;
}
- if (configuration["Prop21"] is string value17 && !string.IsNullOrEmpty(value17))
+ if (TryGetConfigurationValue(configuration, key: "Prop21", out string? value17))
{
- instance.Prop21 = ParseDecimal(value17, configuration.GetSection("Prop21").Path);
+ if (!string.IsNullOrEmpty(value17))
+ {
+ instance.Prop21 = ParseDecimal(value17, configuration.GetSection("Prop21").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop21 = instance.Prop21;
}
- if (configuration["Prop23"] is string value18 && !string.IsNullOrEmpty(value18))
+ if (TryGetConfigurationValue(configuration, key: "Prop23", out string? value18))
{
- instance.Prop23 = ParseSystemTimeSpan(value18, configuration.GetSection("Prop23").Path);
+ if (!string.IsNullOrEmpty(value18))
+ {
+ instance.Prop23 = ParseSystemTimeSpan(value18, configuration.GetSection("Prop23").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop23 = instance.Prop23;
}
- if (configuration["Prop24"] is string value19 && !string.IsNullOrEmpty(value19))
+ if (TryGetConfigurationValue(configuration, key: "Prop24", out string? value19))
{
- instance.Prop24 = ParseSystemGuid(value19, configuration.GetSection("Prop24").Path);
+ if (!string.IsNullOrEmpty(value19))
+ {
+ instance.Prop24 = ParseSystemGuid(value19, configuration.GetSection("Prop24").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop24 = instance.Prop24;
}
- if (configuration["Prop25"] is string value20 && !string.IsNullOrEmpty(value20))
+ if (TryGetConfigurationValue(configuration, key: "Prop25", out string? value20))
{
- instance.Prop25 = ParseSystemUri(value20, configuration.GetSection("Prop25").Path);
+ if (value20 is null)
+ {
+ instance.Prop25 = null;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(value20))
+ {
+ instance.Prop25 = ParseSystemUri(value20, configuration.GetSection("Prop25").Path);
+ }
+ }
}
else if (defaultValueIfNotFound)
{
@@ -265,9 +336,19 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["Prop26"] is string value21 && !string.IsNullOrEmpty(value21))
+ if (TryGetConfigurationValue(configuration, key: "Prop26", out string? value21))
{
- instance.Prop26 = ParseSystemVersion(value21, configuration.GetSection("Prop26").Path);
+ if (value21 is null)
+ {
+ instance.Prop26 = null;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(value21))
+ {
+ instance.Prop26 = ParseSystemVersion(value21, configuration.GetSection("Prop26").Path);
+ }
+ }
}
else if (defaultValueIfNotFound)
{
@@ -278,18 +359,35 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["Prop27"] is string value22 && !string.IsNullOrEmpty(value22))
+ if (TryGetConfigurationValue(configuration, key: "Prop27", out string? value22))
{
- instance.Prop27 = ParseEnum(value22, configuration.GetSection("Prop27").Path);
+ if (!string.IsNullOrEmpty(value22))
+ {
+ instance.Prop27 = ParseEnum(value22, configuration.GetSection("Prop27").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop27 = instance.Prop27;
}
- if (configuration["Prop28"] is string value23 && !string.IsNullOrEmpty(value23))
+ if (TryGetConfigurationValue(configuration, key: "Prop28", out string? value23))
{
- instance.Prop28 = ParseByteArray(value23, configuration.GetSection("Prop28").Path);
+ if (value23 is null)
+ {
+ instance.Prop28 = null;
+ }
+ else
+ {
+ if (value23 == string.Empty)
+ {
+ instance.Prop28 = ParseByteArray(value23, configuration.GetSection("Prop28").Path);
+ }
+ else if (!string.IsNullOrEmpty(value23))
+ {
+ instance.Prop28 = ParseByteArray(value23, configuration.GetSection("Prop28").Path);
+ }
+ }
}
else if (defaultValueIfNotFound)
{
@@ -300,18 +398,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["Prop29"] is string value24 && !string.IsNullOrEmpty(value24))
+ if (TryGetConfigurationValue(configuration, key: "Prop29", out string? value24))
{
- instance.Prop29 = ParseInt(value24, configuration.GetSection("Prop29").Path);
+ if (!string.IsNullOrEmpty(value24))
+ {
+ instance.Prop29 = ParseInt(value24, configuration.GetSection("Prop29").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Prop29 = instance.Prop29;
}
- if (configuration["Prop30"] is string value25 && !string.IsNullOrEmpty(value25))
+ if (TryGetConfigurationValue(configuration, key: "Prop30", out string? value25))
{
- instance.Prop30 = ParseSystemDateTime(value25, configuration.GetSection("Prop30").Path);
+ if (!string.IsNullOrEmpty(value25))
+ {
+ instance.Prop30 = ParseSystemDateTime(value25, configuration.GetSection("Prop30").Path);
+ }
}
else if (defaultValueIfNotFound)
{
@@ -319,6 +423,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -350,7 +466,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(T)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(T)}'.", exception);
}
}
@@ -362,7 +478,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(bool)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(bool)}'.", exception);
}
}
@@ -374,7 +490,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(byte)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(byte)}'.", exception);
}
}
@@ -386,7 +502,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(sbyte)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(sbyte)}'.", exception);
}
}
@@ -398,7 +514,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(char)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(char)}'.", exception);
}
}
@@ -410,7 +526,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(double)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(double)}'.", exception);
}
}
@@ -422,7 +538,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
@@ -434,7 +550,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(short)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(short)}'.", exception);
}
}
@@ -446,7 +562,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(long)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(long)}'.", exception);
}
}
@@ -458,7 +574,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(float)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(float)}'.", exception);
}
}
@@ -470,7 +586,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(ushort)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(ushort)}'.", exception);
}
}
@@ -482,7 +598,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(uint)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(uint)}'.", exception);
}
}
@@ -494,7 +610,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(ulong)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(ulong)}'.", exception);
}
}
@@ -506,7 +622,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(global::System.Globalization.CultureInfo)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(global::System.Globalization.CultureInfo)}'.", exception);
}
}
@@ -518,7 +634,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(global::System.DateTime)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(global::System.DateTime)}'.", exception);
}
}
@@ -530,7 +646,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(global::System.DateTimeOffset)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(global::System.DateTimeOffset)}'.", exception);
}
}
@@ -542,7 +658,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(decimal)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(decimal)}'.", exception);
}
}
@@ -554,7 +670,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(global::System.TimeSpan)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(global::System.TimeSpan)}'.", exception);
}
}
@@ -566,7 +682,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(global::System.Guid)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(global::System.Guid)}'.", exception);
}
}
@@ -578,7 +694,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(global::System.Uri)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(global::System.Uri)}'.", exception);
}
}
@@ -590,7 +706,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(global::System.Version)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(global::System.Version)}'.", exception);
}
}
@@ -602,7 +718,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(byte[])}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(byte[])}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/UnsupportedTypes.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/UnsupportedTypes.generated.txt
index 6b9ae8a88e90f7..2fb36106308427 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/UnsupportedTypes.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/Version1/UnsupportedTypes.generated.txt
@@ -97,7 +97,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance.Add(value);
}
@@ -110,7 +110,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
temp1.Add(value);
}
@@ -130,7 +130,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Options), s_configKeys_Options, configuration, binderOptions);
- if (configuration["Name"] is string value2)
+ if (TryGetConfigurationValue(configuration, key: "Name", out string? value2))
{
instance.Name = value2;
}
@@ -143,45 +143,55 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["Age"] is string value3 && !string.IsNullOrEmpty(value3))
+ if (TryGetConfigurationValue(configuration, key: "Age", out string? value3))
{
- instance.Age = ParseInt(value3, configuration.GetSection("Age").Path);
+ if (!string.IsNullOrEmpty(value3))
+ {
+ instance.Age = ParseInt(value3, configuration.GetSection("Age").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.Age = instance.Age;
}
- if (AsConfigWithChildren(configuration.GetSection("List")) is IConfigurationSection section4)
+ var value4 = configuration.GetSection("List");
+ if (AsConfigWithChildren(value4) is IConfigurationSection section5)
{
- global::System.Collections.Generic.List? temp6 = instance.List;
- temp6 ??= new global::System.Collections.Generic.List();
- BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions);
- instance.List = temp6;
+ global::System.Collections.Generic.List? temp7 = instance.List;
+ temp7 ??= new global::System.Collections.Generic.List();
+ BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
+ instance.List = temp7;
}
else
{
instance.List = instance.List;
}
- if (AsConfigWithChildren(configuration.GetSection("Array")) is IConfigurationSection section7)
+ var value8 = configuration.GetSection("Array");
+ if (AsConfigWithChildren(value8) is IConfigurationSection section9)
{
- string[]? temp9 = instance.Array;
- temp9 ??= new string[0];
- BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
- instance.Array = temp9;
+ string[]? temp11 = instance.Array;
+ temp11 ??= new string[0];
+ BindCore(section9, ref temp11, defaultValueIfNotFound: false, binderOptions);
+ instance.Array = temp11;
}
else
{
instance.Array = instance.Array;
}
+ if (instance.Array is null && TryGetConfigurationValue(value8, key: null, out string? value12) && value12 == string.Empty)
+ {
+ instance.Array = global::System.Array.Empty();
+ }
- if (AsConfigWithChildren(configuration.GetSection("Record")) is IConfigurationSection section10)
+ var value13 = configuration.GetSection("Record");
+ if (AsConfigWithChildren(value13) is IConfigurationSection section14)
{
- global::Record? temp12 = instance.Record;
- temp12 ??= InitializeRecordAction(section10, binderOptions);
- BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions);
- instance.Record = temp12;
+ global::Record? temp16 = instance.Record;
+ temp16 ??= InitializeRecordAction(section14, binderOptions);
+ BindCore(section14, ref temp16, defaultValueIfNotFound: false, binderOptions);
+ instance.Record = temp16;
}
else
{
@@ -192,9 +202,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static global::Record InitializeRecordAction(IConfiguration configuration, BinderOptions? binderOptions)
{
int x = (int)(10);
- if (configuration["x"] is string value13 && !string.IsNullOrEmpty(value13))
+ if (TryGetConfigurationValue(configuration, key: "x", out string? value17))
{
- x = ParseInt(value13, configuration.GetSection("x").Path);
+ if (!string.IsNullOrEmpty(value17))
+ {
+ x = ParseInt(value17, configuration.GetSection("x").Path);
+ }
}
return new global::Record(x)
@@ -203,6 +216,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
};
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -270,7 +295,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind.generated.txt
index 499cd068953bd9..ddc13d85ae3ecc 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind.generated.txt
@@ -87,9 +87,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -98,7 +101,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -121,7 +124,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value0)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value0))
{
instance.MyString = value0;
}
@@ -134,45 +137,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ var value2 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value2) is IConfigurationSection section3)
{
- global::System.Collections.Generic.List? temp4 = instance.MyList;
- temp4 ??= new global::System.Collections.Generic.List();
- BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp4;
+ global::System.Collections.Generic.List? temp5 = instance.MyList;
+ temp5 ??= new global::System.Collections.Generic.List();
+ BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp5;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ var value6 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value6) is IConfigurationSection section7)
{
- global::System.Collections.Generic.Dictionary? temp7 = instance.MyDictionary;
- temp7 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp7;
+ global::System.Collections.Generic.Dictionary? temp9 = instance.MyDictionary;
+ temp9 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp9;
}
else
{
instance.MyDictionary = instance.MyDictionary;
}
- if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
+ var value10 = configuration.GetSection("MyComplexDictionary");
+ if (AsConfigWithChildren(value10) is IConfigurationSection section11)
{
- global::System.Collections.Generic.Dictionary? temp10 = instance.MyComplexDictionary;
- temp10 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyComplexDictionary = temp10;
+ global::System.Collections.Generic.Dictionary? temp13 = instance.MyComplexDictionary;
+ temp13 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
+ instance.MyComplexDictionary = temp13;
}
else
{
@@ -180,6 +189,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -238,7 +259,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind_Instance.generated.txt
index 43edcd50559144..9a3841f52573c2 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind_Instance.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind_Instance.generated.txt
@@ -57,9 +57,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -68,7 +71,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -91,7 +94,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value0)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value0))
{
instance.MyString = value0;
}
@@ -104,45 +107,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ var value2 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value2) is IConfigurationSection section3)
{
- global::System.Collections.Generic.List? temp4 = instance.MyList;
- temp4 ??= new global::System.Collections.Generic.List();
- BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp4;
+ global::System.Collections.Generic.List? temp5 = instance.MyList;
+ temp5 ??= new global::System.Collections.Generic.List();
+ BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp5;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ var value6 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value6) is IConfigurationSection section7)
{
- global::System.Collections.Generic.Dictionary? temp7 = instance.MyDictionary;
- temp7 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions);
- instance.MyDictionary = temp7;
+ global::System.Collections.Generic.Dictionary? temp9 = instance.MyDictionary;
+ temp9 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions);
+ instance.MyDictionary = temp9;
}
else
{
instance.MyDictionary = instance.MyDictionary;
}
- if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
+ var value10 = configuration.GetSection("MyComplexDictionary");
+ if (AsConfigWithChildren(value10) is IConfigurationSection section11)
{
- global::System.Collections.Generic.Dictionary? temp10 = instance.MyComplexDictionary;
- temp10 ??= new global::System.Collections.Generic.Dictionary();
- BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions);
- instance.MyComplexDictionary = temp10;
+ global::System.Collections.Generic.Dictionary? temp13 = instance.MyComplexDictionary;
+ temp13 ??= new global::System.Collections.Generic.Dictionary();
+ BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions);
+ instance.MyComplexDictionary = temp13;
}
else
{
@@ -150,6 +159,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
+ /// Tries to get the configuration value for the specified key.
+ public static bool TryGetConfigurationValue(IConfiguration configuration, string key, out string? value)
+ {
+ if (configuration is ConfigurationSection section)
+ {
+ return section.TryGetValue(key, out value);
+ }
+
+ value = key != null ? configuration[key] : configuration is IConfigurationSection sec ? sec.Value : null;
+ return value != null;
+ }
+
/// If required by the binder options, validates that there are no unknown keys in the input configuration object.
public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions)
@@ -190,7 +211,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
catch (Exception exception)
{
- throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
+ throw new InvalidOperationException($"Failed to convert configuration value '{value ?? "null"}' at '{path}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind_Instance_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind_Instance_BinderOptions.generated.txt
index b6e930a7f3e3da..7e77ce6879fb63 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind_Instance_BinderOptions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ConfigurationBinder/Version1/Bind_Instance_BinderOptions.generated.txt
@@ -57,9 +57,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value && !string.IsNullOrEmpty(value))
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
- instance.Add(ParseInt(value, section.Path));
+ if (!string.IsNullOrEmpty(value))
+ {
+ instance.Add(ParseInt(value, section.Path));
+ }
}
}
}
@@ -68,7 +71,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string value)
+ if (TryGetConfigurationValue(section, key: null, out string? value))
{
instance[section.Key] = value;
}
@@ -91,7 +94,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
ValidateConfigurationKeys(typeof(global::Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
- if (configuration["MyString"] is string value0)
+ if (TryGetConfigurationValue(configuration, key: "MyString", out string? value0))
{
instance.MyString = value0;
}
@@ -104,45 +107,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- if (configuration["MyInt"] is string value1 && !string.IsNullOrEmpty(value1))
+ if (TryGetConfigurationValue(configuration, key: "MyInt", out string? value1))
{
- instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ if (!string.IsNullOrEmpty(value1))
+ {
+ instance.MyInt = ParseInt(value1, configuration.GetSection("MyInt").Path);
+ }
}
else if (defaultValueIfNotFound)
{
instance.MyInt = instance.MyInt;
}
- if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ var value2 = configuration.GetSection("MyList");
+ if (AsConfigWithChildren(value2) is IConfigurationSection section3)
{
- global::System.Collections.Generic.List? temp4 = instance.MyList;
- temp4 ??= new global::System.Collections.Generic.List();
- BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions);
- instance.MyList = temp4;
+ global::System.Collections.Generic.List? temp5 = instance.MyList;
+ temp5 ??= new global::System.Collections.Generic.List();
+ BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions);
+ instance.MyList = temp5;
}
else
{
instance.MyList = instance.MyList;
}
- if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ var value6 = configuration.GetSection("MyDictionary");
+ if (AsConfigWithChildren(value6) is IConfigurationSection section7)
{
- global::System.Collections.Generic.Dictionary