diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs
index f242ca39d29991..f24a43c022ae43 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs
@@ -15,17 +15,13 @@ private sealed partial class Emitter
private readonly SourceProductionContext _context;
private readonly SourceGenerationSpec _sourceGenSpec;
- // Postfix for stringValueX variables used to save config value indexer
- // results e.g. if (configuration["Key"] is string stringValue0) { ... }
- private int _parseValueCount;
-
- private bool _precedingBlockExists;
-
- private readonly SourceWriter _writer = new();
+ private bool _emitBlankLineBeforeNextStatement;
+ private bool _useFullyQualifiedNames;
+ private int _valueSuffixIndex;
private static readonly Regex s_arrayBracketsRegex = new(Regex.Escape("[]"));
- public bool _useFullyQualifiedNames { get; private set; }
+ private readonly SourceWriter _writer = new();
public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSpec)
{
@@ -40,9 +36,13 @@ public void Emit()
return;
}
- _writer.WriteLine(@"//
-#nullable enable
-");
+ _writer.WriteBlock("""
+ //
+ #nullable enable
+ #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
+ """);
+ _writer.WriteBlankLine();
+
_useFullyQualifiedNames = true;
EmitBinder_ConfigurationBinder();
EmitBinder_Extensions_OptionsBuilder();
@@ -54,119 +54,98 @@ public void Emit()
_context.AddSource($"{Identifier.GeneratedConfigurationBinder}.g.cs", _writer.ToSourceText());
}
- private void EmitBindLogicFromRootMethod(TypeSpec type, string expressionForMemberAccess, InitializationKind initKind)
- {
- TypeSpecKind kind = type.SpecKind;
-
- if (kind is TypeSpecKind.Nullable)
- {
- EmitBindLogicFromRootMethod(((NullableSpec)type).UnderlyingType, expressionForMemberAccess, initKind);
- }
- else
- {
- if (type is ParsableFromStringSpec stringParsableType)
- {
- if (initKind is InitializationKind.Declaration)
- {
- EmitCastToIConfigurationSection();
- _writer.WriteLine($"{GetTypeDisplayString(type)} {expressionForMemberAccess} = default!;");
- }
- else
- {
- EmitCastToIConfigurationSection();
- }
-
- EmitBindLogicFromString(stringParsableType, Expression.sectionValue, Expression.sectionPath);
- }
- else
- {
- EmitBindCoreCall(type, expressionForMemberAccess, Identifier.configuration, initKind);
- }
- }
- }
-
private void EmitBindCoreCall(
TypeSpec type,
- string expressionForMemberAccess,
- string expressionForConfigArg,
- InitializationKind initKind)
+ string memberAccessExpr,
+ string configArgExpr,
+ InitializationKind initKind,
+ Action? writeOnSuccess = null)
{
Debug.Assert(type.CanInitialize);
- string tempVarName = GetIncrementalVarName(Identifier.temp);
+ if (!type.NeedsMemberBinding)
+ {
+ EmitObjectInit(memberAccessExpr, initKind);
+ return;
+ }
+
+ string tempIdentifier = GetIncrementalIdentifier(Identifier.temp);
if (initKind is InitializationKind.AssignmentWithNullCheck)
{
- _writer.WriteLine($"{type.MinimalDisplayString} {tempVarName} = {expressionForMemberAccess};");
- EmitObjectInit(type, tempVarName, InitializationKind.AssignmentWithNullCheck);
- EmitBindCoreCall(tempVarName);
+ _writer.WriteLine($"{type.MinimalDisplayString} {tempIdentifier} = {memberAccessExpr};");
+ EmitBindCoreCall(tempIdentifier, InitializationKind.AssignmentWithNullCheck);
}
else if (initKind is InitializationKind.None && type.IsValueType)
{
- EmitObjectInit(type, tempVarName, InitializationKind.Declaration);
- _writer.WriteLine($@"{Identifier.BindCore}({expressionForConfigArg}, ref {tempVarName}, {Identifier.binderOptions});");
- _writer.WriteLine($"{expressionForMemberAccess} = {tempVarName};");
+ EmitBindCoreCall(tempIdentifier, InitializationKind.Declaration);
+ _writer.WriteLine($"{memberAccessExpr} = {tempIdentifier};");
}
else
{
- EmitObjectInit(type, expressionForMemberAccess, initKind);
- EmitBindCoreCall(expressionForMemberAccess);
+ EmitBindCoreCall(memberAccessExpr, initKind);
}
- void EmitBindCoreCall(string varName)
+ void EmitBindCoreCall(string objExpression, InitializationKind initKind)
{
- string bindCoreCall = $@"{GetHelperMethodDisplayString(Identifier.BindCore)}({expressionForConfigArg}, ref {varName}, {Identifier.binderOptions});";
+ string methodDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCore));
+ string bindCoreCall = $@"{methodDisplayString}({configArgExpr}, ref {objExpression}, {Identifier.binderOptions});";
+
+ EmitObjectInit(objExpression, initKind);
_writer.WriteLine(bindCoreCall);
+ writeOnSuccess?.Invoke(objExpression);
+ }
+
+ void EmitObjectInit(string objExpression, InitializationKind initKind)
+ {
+ if (initKind is not InitializationKind.None)
+ {
+ this.EmitObjectInit(type, objExpression, initKind, configArgExpr);
+ }
}
}
- public void EmitBindLogicFromString(
+ private void EmitBindLogicFromString(
ParsableFromStringSpec type,
- string configStringValueExpr,
- string configValuePathExpr,
- Action? writeOnSuccess = null,
- bool isCollectionElement = false)
+ string sectionValueExpr,
+ string sectionPathExpr,
+ Action? writeOnSuccess,
+ bool checkForNullSectionValue,
+ bool useIncrementalStringValueIdentifier)
{
StringParsableTypeKind typeKind = type.StringParsableTypeKind;
Debug.Assert(typeKind is not StringParsableTypeKind.None);
- string stringValueVarName = GetIncrementalVarName(Identifier.stringValue);
- string parsedValueExpr;
+ string nonNull_StringValue_Identifier = useIncrementalStringValueIdentifier ? GetIncrementalIdentifier(Identifier.value) : Identifier.value;
+ string stringValueToParse_Expr = checkForNullSectionValue ? nonNull_StringValue_Identifier : sectionValueExpr;
- if (typeKind is StringParsableTypeKind.ConfigValue)
+ string parsedValueExpr;
+ if (typeKind is StringParsableTypeKind.AssignFromSectionValue)
{
- if (isCollectionElement)
- {
- parsedValueExpr = stringValueVarName;
- }
- else
- {
- writeOnSuccess?.Invoke(configStringValueExpr);
- return;
- }
+ parsedValueExpr = stringValueToParse_Expr;
}
else
{
string helperMethodDisplayString = GetHelperMethodDisplayString(type.ParseMethodName);
- parsedValueExpr = $"{helperMethodDisplayString}({stringValueVarName}, () => {configValuePathExpr})";
+ parsedValueExpr = $"{helperMethodDisplayString}({stringValueToParse_Expr}, () => {sectionPathExpr})";
}
- _writer.WriteBlockStart($"if ({configStringValueExpr} is string {stringValueVarName})");
- writeOnSuccess?.Invoke(parsedValueExpr);
- _writer.WriteBlockEnd();
-
- return;
+ if (!checkForNullSectionValue)
+ {
+ writeOnSuccess?.Invoke(parsedValueExpr);
+ }
+ else
+ {
+ _writer.WriteBlockStart($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})");
+ writeOnSuccess?.Invoke(parsedValueExpr);
+ _writer.WriteBlockEnd();
+ }
}
- private bool EmitObjectInit(TypeSpec type, string expressionForMemberAccess, InitializationKind initKind)
+ private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, InitializationKind initKind, string configArgExpr)
{
- Debug.Assert(type.CanInitialize);
-
- if (initKind is InitializationKind.None)
- {
- return true;
- }
+ Debug.Assert(type.CanInitialize && initKind is not InitializationKind.None);
- string expressionForInit;
+ string initExpr;
CollectionSpec? collectionType = type as CollectionSpec;
string effectiveDisplayString = GetTypeDisplayString(type);
@@ -174,30 +153,29 @@ private bool EmitObjectInit(TypeSpec type, string expressionForMemberAccess, Ini
{
if (collectionType is EnumerableSpec { InitializationStrategy: InitializationStrategy.Array })
{
- expressionForInit = $"new {s_arrayBracketsRegex.Replace(effectiveDisplayString, "[0]", 1)}";
+ initExpr = $"new {s_arrayBracketsRegex.Replace(effectiveDisplayString, "[0]", 1)}";
}
else
{
effectiveDisplayString = GetTypeDisplayString(collectionType.ConcreteType ?? collectionType);
- expressionForInit = $"new {effectiveDisplayString}()";
+ initExpr = $"new {effectiveDisplayString}()";
}
}
else if (type.InitializationStrategy is InitializationStrategy.ParameterlessConstructor)
{
- expressionForInit = $"new {effectiveDisplayString}()";
+ initExpr = $"new {effectiveDisplayString}()";
}
else
{
Debug.Assert(type.InitializationStrategy is InitializationStrategy.ParameterizedConstructor);
- string expressionForConfigSection = initKind is InitializationKind.Declaration ? Identifier.configuration : Identifier.section;
- string initMethodIdentifier = GetHelperMethodDisplayString(((ObjectSpec)type).InitializeMethodDisplayString);
- expressionForInit = $"{initMethodIdentifier}({expressionForConfigSection}, {Identifier.binderOptions});";
+ string initMethodIdentifier = GetInitalizeMethodDisplayString(((ObjectSpec)type));
+ initExpr = $"{initMethodIdentifier}({configArgExpr}, {Identifier.binderOptions})";
}
if (initKind == InitializationKind.Declaration)
{
- Debug.Assert(!expressionForMemberAccess.Contains("."));
- _writer.WriteLine($"var {expressionForMemberAccess} = {expressionForInit};");
+ Debug.Assert(!memberAccessExpr.Contains("."));
+ _writer.WriteLine($"var {memberAccessExpr} = {initExpr};");
}
else if (initKind == InitializationKind.AssignmentWithNullCheck)
{
@@ -208,28 +186,28 @@ private bool EmitObjectInit(TypeSpec type, string expressionForMemberAccess, Ini
{
if (collectionType.InitializationStrategy is InitializationStrategy.ParameterizedConstructor)
{
- _writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : new {effectiveDisplayString}({expressionForMemberAccess});");
+ _writer.WriteLine($"{memberAccessExpr} = {memberAccessExpr} is null ? {initExpr} : new {effectiveDisplayString}({memberAccessExpr});");
}
else
{
- _writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : {expressionForMemberAccess}.{collectionType.ToEnumerableMethodCall!};");
+ _writer.WriteLine($"{memberAccessExpr} = {memberAccessExpr} is null ? {initExpr} : {memberAccessExpr}.{collectionType.ToEnumerableMethodCall!};");
}
}
else
{
- _writer.WriteLine($"{expressionForMemberAccess} ??= {expressionForInit};");
+ _writer.WriteLine($"{memberAccessExpr} ??= {initExpr};");
}
}
else
{
Debug.Assert(initKind is InitializationKind.SimpleAssignment);
- _writer.WriteLine($"{expressionForMemberAccess} = {expressionForInit};");
+ _writer.WriteLine($"{memberAccessExpr} = {initExpr};");
}
return true;
}
- public void EmitCastToIConfigurationSection()
+ private void EmitCastToIConfigurationSection()
{
string sectionTypeDisplayString;
string exceptionTypeDisplayString;
@@ -252,7 +230,7 @@ public void EmitCastToIConfigurationSection()
""");
}
- public void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn)
+ private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn)
{
string returnPostfix = voidReturn ? string.Empty : " null";
string methodDisplayString = GetHelperMethodDisplayString(Identifier.HasValueOrChildren);
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs
index 41745204778bde..9127046f22c5e8 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs
@@ -99,18 +99,14 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
if (IsNullable(type, out ITypeSymbol? underlyingType))
{
spec = TryGetTypeSpec(underlyingType, Diagnostics.NullableUnderlyingTypeNotSupported, out TypeSpec? underlyingTypeSpec)
- ? new NullableSpec(type) { Location = location, UnderlyingType = underlyingTypeSpec }
+ ? new NullableSpec(type, underlyingTypeSpec)
: null;
}
else if (IsParsableFromString(type, out StringParsableTypeKind specialTypeKind))
{
- ParsableFromStringSpec stringParsableSpec = new(type)
- {
- Location = location,
- StringParsableTypeKind = specialTypeKind
- };
+ ParsableFromStringSpec stringParsableSpec = new(type) { StringParsableTypeKind = specialTypeKind };
- if (stringParsableSpec.StringParsableTypeKind is not StringParsableTypeKind.ConfigValue)
+ if (stringParsableSpec.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue)
{
_sourceGenSpec.PrimitivesForHelperGen.Add(stringParsableSpec);
}
@@ -119,17 +115,15 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}
else if (IsSupportedArrayType(type, location))
{
- spec = CreateArraySpec((type as IArrayTypeSymbol)!, location);
- RegisterBindCoreGenType(spec);
+ spec = CreateArraySpec((type as IArrayTypeSymbol));
}
else if (IsCollection(type))
{
spec = CreateCollectionSpec((INamedTypeSymbol)type, location);
- RegisterBindCoreGenType(spec);
}
else if (SymbolEqualityComparer.Default.Equals(type, _typeSymbols.IConfigurationSection))
{
- spec = new ConfigurationSectionSpec(type) { Location = location };
+ spec = new ConfigurationSectionSpec(type);
}
else if (type is INamedTypeSymbol namedType)
{
@@ -138,7 +132,6 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
_sourceGenSpec.TypeNamespaces.Add("System.Collections.Generic");
spec = CreateObjectSpec(namedType, location);
- RegisterBindCoreGenType(spec);
}
if (spec is null)
@@ -154,14 +147,6 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}
return _createdSpecs[type] = spec;
-
- void RegisterBindCoreGenType(TypeSpec? spec)
- {
- if (spec is not null)
- {
- RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, spec);
- }
- }
}
private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, TypeSpec type)
@@ -177,8 +162,11 @@ private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, Typ
private void RegisterTypeForBindCoreUntypedGen(TypeSpec typeSpec)
{
- RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, typeSpec);
- RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCoreUntyped, typeSpec);
+ if (typeSpec.NeedsMemberBinding)
+ {
+ RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, typeSpec);
+ RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCoreUntyped, typeSpec);
+ }
}
private static bool IsNullable(ITypeSymbol type, [NotNullWhen(true)] out ITypeSymbol? underlyingType)
@@ -222,7 +210,7 @@ private bool IsParsableFromString(ITypeSymbol type, out StringParsableTypeKind t
case SpecialType.System_String:
case SpecialType.System_Object:
{
- typeKind = StringParsableTypeKind.ConfigValue;
+ typeKind = StringParsableTypeKind.AssignFromSectionValue;
return true;
}
case SpecialType.System_Boolean:
@@ -312,7 +300,7 @@ private bool TryGetTypeSpec(ITypeSymbol type, DiagnosticDescriptor descriptor, o
return true;
}
- private EnumerableSpec? CreateArraySpec(IArrayTypeSymbol arrayType, Location? location)
+ private EnumerableSpec? CreateArraySpec(IArrayTypeSymbol arrayType)
{
if (!TryGetTypeSpec(arrayType.ElementType, Diagnostics.ElementTypeNotSupported, out TypeSpec elementSpec))
{
@@ -325,7 +313,6 @@ private bool TryGetTypeSpec(ITypeSymbol type, DiagnosticDescriptor descriptor, o
EnumerableSpec spec = new EnumerableSpec(arrayType)
{
- Location = location,
ElementType = elementSpec,
ConcreteType = listSpec,
InitializationStrategy = InitializationStrategy.Array,
@@ -334,6 +321,8 @@ private bool TryGetTypeSpec(ITypeSymbol type, DiagnosticDescriptor descriptor, o
};
Debug.Assert(spec.CanInitialize);
+ RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, spec);
+
return spec;
}
@@ -368,6 +357,7 @@ private bool IsSupportedArrayType(ITypeSymbol type, Location? location)
if (spec is not null)
{
+ RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, spec);
spec.InitExceptionMessage ??= spec.ElementType.InitExceptionMessage;
}
@@ -436,7 +426,6 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? loc
DictionarySpec spec = new(type)
{
- Location = location,
KeyType = (ParsableFromStringSpec)keySpec,
ElementType = elementSpec,
InitializationStrategy = constructionStrategy,
@@ -523,11 +512,10 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? loc
return null;
}
- RegisterHasChildrenHelperForGenIfRequired(elementSpec);
+ Register_AsConfigWithChildren_HelperForGen_IfRequired(elementSpec);
EnumerableSpec spec = new(type)
{
- Location = location,
ElementType = elementSpec,
InitializationStrategy = constructionStrategy,
PopulationStrategy = populationStrategy,
@@ -544,7 +532,7 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? loc
private ObjectSpec? CreateObjectSpec(INamedTypeSymbol type, Location? location)
{
// Add spec to cache before traversing properties to avoid stack overflow.
- ObjectSpec objectSpec = new(type) { Location = location };
+ ObjectSpec objectSpec = new(type);
_createdSpecs.Add(type, objectSpec);
string typeName = objectSpec.Name;
@@ -627,7 +615,7 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? loc
{
PropertySpec spec = new(property) { Type = propertyTypeSpec, ConfigurationKeyName = configKeyName };
objectSpec.Properties[propertyName] = spec;
- RegisterHasChildrenHelperForGenIfRequired(propertyTypeSpec);
+ Register_AsConfigWithChildren_HelperForGen_IfRequired(propertyTypeSpec);
}
}
}
@@ -691,17 +679,22 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? loc
Debug.Assert((objectSpec.CanInitialize && objectSpec.InitExceptionMessage is null) ||
(!objectSpec.CanInitialize && objectSpec.InitExceptionMessage is not null));
+ if (objectSpec.NeedsMemberBinding)
+ {
+ RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, objectSpec);
+ }
+
return objectSpec;
}
- private void RegisterHasChildrenHelperForGenIfRequired(TypeSpec type)
+ private void Register_AsConfigWithChildren_HelperForGen_IfRequired(TypeSpec type)
{
if (type.SpecKind is TypeSpecKind.Object or
TypeSpecKind.Enumerable or
TypeSpecKind.Dictionary)
{
- _sourceGenSpec.ShouldEmitHasChildren = true;
+ _sourceGenSpec.MethodsToGen_CoreBindingHelper |= MethodsToGen_CoreBindingHelper.AsConfigWithChildren;
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs
index a90eacd4837c52..d71e414af19dc7 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs
@@ -24,21 +24,20 @@ private void EmitBinder_ConfigurationBinder()
return;
}
- _writer.WriteLine("/// Generated helper providing an AOT and linking compatible implementation for configuration binding.");
- _writer.WriteBlockStart($"internal static class {Identifier.GeneratedConfigurationBinder}");
+ _emitBlankLineBeforeNextStatement = false;
+ EmitRootBindingClassBlockStart(Identifier.GeneratedConfigurationBinder);
EmitGetMethods();
EmitGetValueMethods();
EmitBindMethods_ConfigurationBinder();
_writer.WriteBlockEnd();
-
- _precedingBlockExists = true;
+ _emitBlankLineBeforeNextStatement = true;
}
private void EmitGetMethods()
{
- const string expressionForGetCore = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{Identifier.GetCore}";
+ const string expressionForGetCore = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{nameof(MethodsToGen_CoreBindingHelper.GetCore)}";
const string documentation = "Attempts to bind the configuration instance to a new instance of type T.";
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_T))
@@ -72,7 +71,7 @@ private void EmitGetMethods()
private void EmitGetValueMethods()
{
- const string expressionForGetValueCore = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{Identifier.GetValueCore}";
+ const string expressionForGetValueCore = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{nameof(MethodsToGen_CoreBindingHelper.GetValueCore)}";
const string documentation = "Extracts the value with the specified key and converts it to the specified type.";
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_T_key))
@@ -144,7 +143,7 @@ private void EmitBindMethods_ConfigurationBinder()
EmitMethodImplementation(
type,
additionalParams: $"string {Identifier.key}, {GetObjParameter(type)}",
- configExpression: $"{Identifier.configuration}.{Identifier.GetSection}({Identifier.key})",
+ configExpression: $"{Expression.configurationGetSection}({Identifier.key})",
configureOptions: false);
}
}
@@ -152,9 +151,18 @@ private void EmitBindMethods_ConfigurationBinder()
void EmitMethodImplementation(TypeSpec type, string additionalParams, string configExpression, bool configureOptions)
{
string binderOptionsArg = configureOptions ? $"{Expression.GetBinderOptions}({Identifier.configureOptions})" : $"{Identifier.binderOptions}: null";
- string returnExpression = type.CanInitialize
- ? $"{FullyQualifiedDisplayString.CoreBindingHelper}.{Identifier.BindCore}({configExpression}, ref {Identifier.obj}, {binderOptionsArg})"
- : GetInitException(type.InitExceptionMessage);
+
+ string returnExpression;
+ if (type.CanInitialize)
+ {
+ returnExpression = type.NeedsMemberBinding
+ ? $"{FullyQualifiedDisplayString.CoreBindingHelper}.{nameof(MethodsToGen_CoreBindingHelper.BindCore)}({configExpression}, ref {Identifier.obj}, {binderOptionsArg})"
+ : "{ }";
+ }
+ else
+ {
+ returnExpression = GetInitException(type.InitExceptionMessage);
+ }
StartMethodDefinition("Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.");
_writer.WriteLine($"public static void {Identifier.Bind}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {additionalParams}) => "
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs
index 04b811c3bc17d5..bdf56060b82dbe 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs
@@ -18,18 +18,23 @@ private sealed partial class Emitter
private void Emit_CoreBindingHelper()
{
- Debug.Assert(_precedingBlockExists);
+ Debug.Assert(_emitBlankLineBeforeNextStatement);
_writer.WriteBlankLine();
- _precedingBlockExists = false;
+ _emitBlankLineBeforeNextStatement = false;
_writer.WriteBlockStart($"namespace {ProjectName}");
EmitHelperUsingStatements();
_writer.WriteBlankLine();
- _writer.WriteLine("/// Provide core binding logic.");
- _writer.WriteBlockStart($"internal static class {Identifier.CoreBindingHelper}");
+ _writer.WriteBlock($$"""
+ /// Provide core binding logic.
+ {{GetGeneratedCodeAttributeSrc()}}
+ file static class {{Identifier.CoreBindingHelper}}
+ {
+ """);
+ EmitConfigurationKeyCaches();
EmitGetCoreMethod();
EmitGetValueCoreMethod();
EmitBindCoreUntypedMethod();
@@ -49,6 +54,32 @@ private void EmitHelperUsingStatements()
}
}
+ private void EmitConfigurationKeyCaches()
+ {
+ if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(MethodsToGen_CoreBindingHelper.BindCore, out HashSet targetTypes))
+ {
+ return;
+ }
+
+ foreach (TypeSpec type in targetTypes)
+ {
+ if (type is not ObjectSpec objectType)
+ {
+ continue;
+ }
+
+ HashSet keys = new(objectType.ConstructorParameters.Select(m => GetCacheElement(m)));
+ keys.UnionWith(objectType.Properties.Values.Select(m => GetCacheElement(m)));
+ static string GetCacheElement(MemberSpec member) => $@"""{member.ConfigurationKeyName}""";
+
+ string configKeysSource = string.Join(", ", keys);
+ string fieldName = GetConfigKeyCacheFieldName(objectType);
+ _writer.WriteLine($@"private readonly static Lazy<{MinimalDisplayString.HashSetOfString}> {fieldName} = new(() => new {MinimalDisplayString.HashSetOfString}(StringComparer.OrdinalIgnoreCase) {{ {configKeysSource} }});");
+ }
+
+ _emitBlankLineBeforeNextStatement = true;
+ }
+
private void EmitGetCoreMethod()
{
if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(MethodsToGen_CoreBindingHelper.GetCore, out HashSet? types))
@@ -56,7 +87,8 @@ private void EmitGetCoreMethod()
return;
}
- _writer.WriteBlockStart($"public static object? {Identifier.GetCore}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, Action<{Identifier.BinderOptions}>? {Identifier.configureOptions})");
+ EmitBlankLineIfRequired();
+ _writer.WriteBlockStart($"public static object? {nameof(MethodsToGen_CoreBindingHelper.GetCore)}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, Action<{Identifier.BinderOptions}>? {Identifier.configureOptions})");
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
@@ -67,11 +99,24 @@ private void EmitGetCoreMethod()
foreach (TypeSpec type in types)
{
+ TypeSpecKind kind = type.SpecKind;
+
_writer.WriteBlockStart($"if (type == typeof({type.MinimalDisplayString}))");
- if (type.InitializationStrategy is InitializationStrategy.None || !EmitInitException(type))
+ if (type is ParsableFromStringSpec stringParsableType)
{
- EmitBindLogicFromRootMethod(type, Identifier.obj, InitializationKind.Declaration);
+ EmitCastToIConfigurationSection();
+ EmitBindLogicFromString(
+ stringParsableType,
+ Expression.sectionValue,
+ Expression.sectionPath,
+ writeOnSuccess: parsedValueExpr => _writer.WriteLine($"return {parsedValueExpr};"),
+ checkForNullSectionValue: stringParsableType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue,
+ useIncrementalStringValueIdentifier: false);
+ }
+ else if (!EmitInitException(type))
+ {
+ EmitBindCoreCall(type, Identifier.obj, Identifier.configuration, InitializationKind.Declaration);
_writer.WriteLine($"return {Identifier.obj};");
}
@@ -81,7 +126,7 @@ private void EmitGetCoreMethod()
Emit_NotSupportedException_TypeNotDetectedAsInput();
_writer.WriteBlockEnd();
- _precedingBlockExists = true;
+ _emitBlankLineBeforeNextStatement = true;
}
private void EmitGetValueCoreMethod()
@@ -92,25 +137,32 @@ private void EmitGetValueCoreMethod()
}
EmitBlankLineIfRequired();
-
- _writer.WriteBlockStart($"public static object? {Identifier.GetValueCore}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key})");
+ _writer.WriteBlockStart($"public static object? {nameof(MethodsToGen_CoreBindingHelper.GetValueCore)}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key})");
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
+ _writer.WriteLine($@"{Identifier.IConfigurationSection} {Identifier.section} = {GetSectionFromConfigurationExpression(Identifier.key, addQuotes: false)};");
+ _writer.WriteBlankLine();
- _writer.WriteLine($"{Identifier.IConfigurationSection} {Identifier.section} = {Identifier.configuration}.{Identifier.GetSection}({Identifier.key});");
+ _writer.WriteBlock($$"""
+ if ({{Expression.sectionValue}} is not string {{Identifier.value}})
+ {
+ return null;
+ }
+ """);
_writer.WriteBlankLine();
foreach (TypeSpec type in targetTypes)
{
- ParsableFromStringSpec effectiveType = (ParsableFromStringSpec)((type as NullableSpec)?.UnderlyingType ?? type);
_writer.WriteBlockStart($"if ({Identifier.type} == typeof({type.MinimalDisplayString}))");
EmitBindLogicFromString(
- effectiveType,
- Expression.sectionValue,
- Expression.sectionPath,
- writeOnSuccess: (parsedValueExpr) => _writer.WriteLine($"return {parsedValueExpr};"));
+ (ParsableFromStringSpec)type.EffectiveType,
+ Identifier.value,
+ Expression.sectionPath,
+ writeOnSuccess: (parsedValueExpr) => _writer.WriteLine($"return {parsedValueExpr};"),
+ checkForNullSectionValue: false,
+ useIncrementalStringValueIdentifier: false);
_writer.WriteBlockEnd();
_writer.WriteBlankLine();
@@ -118,7 +170,7 @@ private void EmitGetValueCoreMethod()
_writer.WriteLine("return null;");
_writer.WriteBlockEnd();
- _precedingBlockExists = true;
+ _emitBlankLineBeforeNextStatement = true;
}
private void EmitBindCoreUntypedMethod()
@@ -130,7 +182,7 @@ private void EmitBindCoreUntypedMethod()
EmitBlankLineIfRequired();
- _writer.WriteBlockStart($"public static void {Identifier.BindCoreUntyped}(this {Identifier.IConfiguration} {Identifier.configuration}, object {Identifier.obj}, Type {Identifier.type}, {MinimalDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})");
+ _writer.WriteBlockStart($"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}(this {Identifier.IConfiguration} {Identifier.configuration}, object {Identifier.obj}, Type {Identifier.type}, {MinimalDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})");
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
@@ -143,10 +195,11 @@ private void EmitBindCoreUntypedMethod()
{
_writer.WriteBlockStart($"if (type == typeof({type.MinimalDisplayString}))");
- if (type.InitializationStrategy is InitializationStrategy.None || !EmitInitException(type))
+ TypeSpec effectiveType = type.EffectiveType;
+ if (!EmitInitException(effectiveType))
{
- _writer.WriteLine($"var {Identifier.temp} = ({type.MinimalDisplayString}){Identifier.obj};");
- EmitBindLogicFromRootMethod(type, Identifier.temp, InitializationKind.None);
+ _writer.WriteLine($"var {Identifier.temp} = ({effectiveType.MinimalDisplayString}){Identifier.obj};");
+ EmitBindCoreCall(type, Identifier.temp, Identifier.configuration, InitializationKind.None);
_writer.WriteLine($"return;");
}
@@ -156,7 +209,7 @@ private void EmitBindCoreUntypedMethod()
Emit_NotSupportedException_TypeNotDetectedAsInput();
_writer.WriteBlockEnd();
- _precedingBlockExists = true;
+ _emitBlankLineBeforeNextStatement = true;
}
private void EmitBindCoreMethods()
@@ -168,11 +221,7 @@ private void EmitBindCoreMethods()
foreach (TypeSpec type in targetTypes)
{
- if (type.SpecKind is TypeSpecKind.ParsableFromString)
- {
- continue;
- }
-
+ Debug.Assert(type.NeedsMemberBinding);
EmitBlankLineIfRequired();
EmitBindCoreMethod(type);
}
@@ -180,14 +229,35 @@ private void EmitBindCoreMethods()
private void EmitBindCoreMethod(TypeSpec type)
{
- if (!type.CanInitialize)
+ Debug.Assert(type.CanInitialize);
+
+ string objParameterExpression = $"ref {type.MinimalDisplayString} {Identifier.obj}";
+ _writer.WriteBlockStart(@$"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCore)}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression}, {Identifier.BinderOptions}? {Identifier.binderOptions})");
+
+ EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType);
+
+ TypeSpec effectiveType = type.EffectiveType;
+ if (effectiveType is EnumerableSpec enumerable)
{
- return;
+ if (effectiveType.InitializationStrategy is InitializationStrategy.Array)
+ {
+ Debug.Assert(type == effectiveType);
+ EmitPopulationImplForArray((EnumerableSpec)type);
+ }
+ else
+ {
+ EmitPopulationImplForEnumerableWithAdd(enumerable);
+ }
+ }
+ else if (effectiveType is DictionarySpec dictionary)
+ {
+ EmitBindCoreImplForDictionary(dictionary);
+ }
+ else
+ {
+ EmitBindCoreImplForObject((ObjectSpec)effectiveType);
}
- string objParameterExpression = $"ref {type.MinimalDisplayString} {Identifier.obj}";
- _writer.WriteBlockStart(@$"public static void {Identifier.BindCore}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression}, {Identifier.BinderOptions}? {Identifier.binderOptions})");
- EmitBindCoreImpl(type);
_writer.WriteBlockEnd();
}
@@ -208,84 +278,39 @@ private void EmitInitializeMethods()
private void EmitInitializeMethod(ObjectSpec type)
{
Debug.Assert(type.CanInitialize);
-
List ctorParams = type.ConstructorParameters;
- IEnumerable initOnlyProps = type.Properties.Values.Where(prop => prop.SetOnInit);
+ IEnumerable initOnlyProps = type.Properties.Values.Where(prop => prop is { SetOnInit: true });
+ List ctorArgList = new();
string displayString = type.MinimalDisplayString;
- _writer.WriteBlockStart($"public static {displayString} {type.InitializeMethodDisplayString}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})");
+ _writer.WriteBlockStart($"public static {type.MinimalDisplayString} {GetInitalizeMethodDisplayString(type)}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})");
+ _emitBlankLineBeforeNextStatement = false;
foreach (ParameterSpec parameter in ctorParams)
{
- if (!parameter.HasExplicitDefaultValue)
+ string name = parameter.Name;
+ string argExpr = parameter.RefKind switch
{
- _writer.WriteLine($@"({parameter.Type.MinimalDisplayString} {Identifier.Value}, bool {Identifier.HasConfig}) {parameter.Name} = ({parameter.DefaultValue}, false);");
- }
- else
- {
- _writer.WriteLine($@"{parameter.Type.MinimalDisplayString} {parameter.Name} = {parameter.DefaultValue};");
- }
- }
-
- foreach (PropertySpec property in initOnlyProps)
- {
- if (property.MatchingCtorParam is null)
- {
- _writer.WriteLine($@"{property.Type.MinimalDisplayString} {property.Name} = default!;");
- }
- }
-
- _writer.WriteBlankLine();
-
- _writer.WriteBlock($$"""
- foreach ({{Identifier.IConfigurationSection}} {{Identifier.section}} in {{Identifier.configuration}}.{{Identifier.GetChildren}}())
- {
- switch ({{Expression.sectionKey}})
- {
- """);
-
- List argumentList = new();
+ RefKind.None => name,
+ RefKind.Ref => $"ref {name}",
+ RefKind.Out => "out _",
+ RefKind.In => $"in {name}",
+ _ => throw new InvalidOperationException()
+ };
- foreach (ParameterSpec parameter in ctorParams)
- {
- EmitMemberBindLogic(parameter.Name, parameter.Type, parameter.ConfigurationKeyName, configValueMustExist: !parameter.HasExplicitDefaultValue);
- argumentList.Add(GetExpressionForArgument(parameter));
+ ctorArgList.Add(argExpr);
+ EmitBindImplForMember(parameter);
}
foreach (PropertySpec property in initOnlyProps)
{
if (property.ShouldBind() && property.MatchingCtorParam is null)
{
- EmitMemberBindLogic(property.Name, property.Type, property.ConfigurationKeyName);
+ EmitBindImplForMember(property);
}
}
- EmitSwitchDefault("continue;", addBreak: false);
-
- _writer.WriteBlockEnd();
- _writer.WriteBlockEnd();
-
- _precedingBlockExists = true;
-
- foreach (ParameterSpec parameter in ctorParams)
- {
- if (!parameter.HasExplicitDefaultValue)
- {
- string parameterName = parameter.Name;
-
- EmitBlankLineIfRequired();
- _writer.WriteBlock($$"""
- if (!{{parameterName}}.{{Identifier.HasConfig}})
- {
- throw new {{GetInvalidOperationDisplayName()}}("{{string.Format(ExceptionMessages.ParameterHasNoMatchingConfig, type.Name, parameterName)}}");
- }
- """);
- }
- }
-
- EmitBlankLineIfRequired();
-
- string returnExpression = $"return new {displayString}({string.Join(", ", argumentList)})";
+ string returnExpression = $"return new {displayString}({string.Join(", ", ctorArgList)})";
if (!initOnlyProps.Any())
{
_writer.WriteLine($"{returnExpression};");
@@ -296,87 +321,104 @@ private void EmitInitializeMethod(ObjectSpec type)
foreach (PropertySpec property in initOnlyProps)
{
string propertyName = property.Name;
- string initValue = propertyName + (property.MatchingCtorParam is null or ParameterSpec { HasExplicitDefaultValue: true } ? string.Empty : $".{Identifier.Value}");
- _writer.WriteLine($@"{propertyName} = {initValue},");
+ _writer.WriteLine($@"{propertyName} = {propertyName},");
}
_writer.WriteBlockEnd(";");
}
// End method.
_writer.WriteBlockEnd();
+ _emitBlankLineBeforeNextStatement = true;
- void EmitMemberBindLogic(string memberName, TypeSpec memberType, string configurationKeyName, bool configValueMustExist = false)
+ void EmitBindImplForMember(MemberSpec member)
{
- string lhs = memberName + (configValueMustExist ? $".{Identifier.Value}" : string.Empty);
-
- _writer.WriteLine($@"case ""{configurationKeyName}"":");
- _writer.Indentation++;
- _writer.WriteBlockStart();
+ TypeSpec memberType = member.Type;
+ bool errorOnFailedBinding = member.ErrorOnFailedBinding;
- EmitMemberBindLogicCore(memberType, lhs);
+ string parsedMemberIdentifierDeclarationPrefix = $"{memberType.MinimalDisplayString} {member.Name}";
+ string parsedMemberIdentifier;
- if (configValueMustExist)
+ if (memberType is ParsableFromStringSpec { StringParsableTypeKind: StringParsableTypeKind.AssignFromSectionValue })
{
- _writer.WriteLine($"{memberName}.{Identifier.HasConfig} = true;");
- }
-
- _writer.WriteBlockEnd();
- _writer.WriteLine("break;");
- _writer.Indentation--;
-
- void EmitMemberBindLogicCore(TypeSpec type, string lhs)
- {
- TypeSpecKind kind = type.SpecKind;
+ parsedMemberIdentifier = parsedMemberIdentifierDeclarationPrefix;
- if (kind is TypeSpecKind.Nullable)
+ if (errorOnFailedBinding)
{
- EmitMemberBindLogicCore(((NullableSpec)type).UnderlyingType, lhs);
+ string condition = $@" if ({Identifier.configuration}[""{member.ConfigurationKeyName}""] is not {memberType.MinimalDisplayString} {member.Name})";
+ EmitThrowBlock(condition);
+ _writer.WriteBlankLine();
+ return;
}
- else if (type is ParsableFromStringSpec stringParsableType)
+ }
+ else
+ {
+ parsedMemberIdentifier = member.Name;
+
+ string declarationSuffix;
+ if (errorOnFailedBinding)
{
- EmitBindLogicFromString(
- stringParsableType,
- Expression.sectionValue,
- Expression.sectionPath,
- (parsedValueExpr) => _writer.WriteLine($"{lhs} = {parsedValueExpr}!;"));
+ declarationSuffix = ";";
}
- else if (!EmitInitException(type))
+ else
{
- EmitBindCoreCall(type, lhs, Identifier.section, InitializationKind.SimpleAssignment);
+ string bangExpr = memberType.IsValueType ? string.Empty : "!";
+ declarationSuffix = memberType.CanInitialize
+ ? $" = {member.DefaultValueExpr}{bangExpr};"
+ : ";";
}
+
+ string parsedMemberIdentifierDeclaration = $"{parsedMemberIdentifierDeclarationPrefix}{declarationSuffix}";
+ _writer.WriteLine(parsedMemberIdentifierDeclaration);
+ _emitBlankLineBeforeNextStatement = false;
}
- }
- static string GetExpressionForArgument(ParameterSpec parameter)
- {
- string name = parameter.Name + (parameter.HasExplicitDefaultValue ? string.Empty : $".{Identifier.Value}");
+ bool canBindToMember = this.EmitBindImplForMember(
+ member,
+ parsedMemberIdentifier,
+ sectionPathExpr: GetSectionPathFromConfigurationExpression(member.ConfigurationKeyName),
+ canSet: true);
- return parameter.RefKind switch
+ if (canBindToMember)
{
- RefKind.None => name,
- RefKind.Ref => $"ref {name}",
- RefKind.Out => "out _",
- RefKind.In => $"in {name}",
- _ => throw new InvalidOperationException()
- };
+ if (errorOnFailedBinding)
+ {
+ // Add exception logic for parameter ctors; must be present in configuration object.
+ EmitThrowBlock(condition: "else");
+ }
+
+ _writer.WriteBlankLine();
+ }
+
+ void EmitThrowBlock(string condition) =>
+ _writer.WriteBlock($$"""
+ {{condition}}
+ {
+ throw new {{GetInvalidOperationDisplayName()}}("{{string.Format(ExceptionMessages.ParameterHasNoMatchingConfig, type.Name, member.Name)}}");
+ }
+ """);
}
}
private void EmitHelperMethods()
{
+ if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore))
+ {
+ EmitValidateConfigurationKeysMethod();
+ }
+
if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCoreUntyped | MethodsToGen_CoreBindingHelper.GetCore))
{
_writer.WriteBlankLine();
EmitHasValueOrChildrenMethod();
_writer.WriteBlankLine();
- EmitHasChildrenMethod();
- _precedingBlockExists = true;
+ EmitAsConfigWithChildrenMethod();
+ _emitBlankLineBeforeNextStatement = true;
}
- else if (_sourceGenSpec.ShouldEmitHasChildren)
+ else if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.AsConfigWithChildren))
{
_writer.WriteBlankLine();
- EmitHasChildrenMethod();
- _precedingBlockExists = true;
+ EmitAsConfigWithChildrenMethod();
+ _emitBlankLineBeforeNextStatement = true;
}
if (ShouldEmitMethods(
@@ -385,7 +427,7 @@ private void EmitHelperMethods()
{
_writer.WriteBlankLine();
EmitGetBinderOptionsHelper();
- _precedingBlockExists = true;
+ _emitBlankLineBeforeNextStatement = true;
}
foreach (ParsableFromStringSpec type in _sourceGenSpec.PrimitivesForHelperGen)
@@ -395,6 +437,37 @@ private void EmitHelperMethods()
}
}
+ private void EmitValidateConfigurationKeysMethod()
+ {
+ const string keysIdentifier = "keys";
+ string exceptionMessage = string.Format(ExceptionMessages.MissingConfig, Identifier.ErrorOnUnknownConfiguration, Identifier.BinderOptions, $"{{{Identifier.type}}}", $@"{{string.Join("", "", {Identifier.temp})}}");
+
+ EmitBlankLineIfRequired();
+ _writer.WriteBlock($$"""
+ /// If required by the binder options, validates that there are no unknown keys in the input configuration object.
+ public static void {{Identifier.ValidateConfigurationKeys}}(Type {{Identifier.type}}, {{MinimalDisplayString.LazyHashSetOfString}} {{keysIdentifier}}, {{Identifier.IConfiguration}} {{Identifier.configuration}}, {{Identifier.BinderOptions}}? {{Identifier.binderOptions}})
+ {
+ if ({{Identifier.binderOptions}}?.{{Identifier.ErrorOnUnknownConfiguration}} is true)
+ {
+ {{MinimalDisplayString.ListOfString}}? {{Identifier.temp}} = null;
+
+ foreach ({{Identifier.IConfigurationSection}} {{Identifier.section}} in {{Identifier.configuration}}.{{Identifier.GetChildren}}())
+ {
+ if (!{{keysIdentifier}}.Value.Contains({{Expression.sectionKey}}))
+ {
+ ({{Identifier.temp}} ??= new {{MinimalDisplayString.ListOfString}}()).Add($"'{{{Expression.sectionKey}}}'");
+ }
+ }
+
+ if ({{Identifier.temp}} is not null)
+ {
+ throw new InvalidOperationException($"{{exceptionMessage}}");
+ }
+ }
+ }
+ """);
+ }
+
private void EmitHasValueOrChildrenMethod()
{
_writer.WriteBlock($$"""
@@ -404,21 +477,21 @@ private void EmitHasValueOrChildrenMethod()
{
return true;
}
- return {{Identifier.HasChildren}}({{Identifier.configuration}});
+ return {{Identifier.AsConfigWithChildren}}({{Identifier.configuration}}) is not null;
}
""");
}
- private void EmitHasChildrenMethod()
+ private void EmitAsConfigWithChildrenMethod()
{
_writer.WriteBlock($$"""
- public static bool {{Identifier.HasChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
+ public static {{Identifier.IConfiguration}}? {{Identifier.AsConfigWithChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
{
- foreach ({{Identifier.IConfigurationSection}} {{Identifier.section}} in {{Identifier.configuration}}.{{Identifier.GetChildren}}())
+ foreach ({{Identifier.IConfigurationSection}} _ in {{Identifier.configuration}}.{{Identifier.GetChildren}}())
{
- return true;
+ return {{Identifier.configuration}};
}
- return false;
+ return null;
}
""");
}
@@ -465,52 +538,52 @@ private void EmitPrimitiveParseMethod(ParsableFromStringSpec type)
numberStylesTypeDisplayString = "NumberStyles";
}
- string invariantCultureExpression = $"{cultureInfoTypeDisplayString}.InvariantCulture";
-
- string expressionForParsedValue;
StringParsableTypeKind typeKind = type.StringParsableTypeKind;
string typeDisplayString = type.MinimalDisplayString;
+ string invariantCultureExpression = $"{cultureInfoTypeDisplayString}.InvariantCulture";
+
+ string parsedValueExpr;
switch (typeKind)
{
case StringParsableTypeKind.Enum:
{
- expressionForParsedValue = $"({typeDisplayString}){Identifier.Enum}.{Identifier.Parse}(typeof({typeDisplayString}), {Identifier.stringValue}, ignoreCase: true)";
+ parsedValueExpr = $"({typeDisplayString}){Identifier.Enum}.{Identifier.Parse}(typeof({typeDisplayString}), {Identifier.value}, ignoreCase: true)";
}
break;
case StringParsableTypeKind.ByteArray:
{
- expressionForParsedValue = $"Convert.FromBase64String({Identifier.stringValue})";
+ parsedValueExpr = $"Convert.FromBase64String({Identifier.value})";
}
break;
case StringParsableTypeKind.Integer:
{
- expressionForParsedValue = $"{typeDisplayString}.{Identifier.Parse}({Identifier.stringValue}, {numberStylesTypeDisplayString}.Integer, {invariantCultureExpression})";
+ parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {numberStylesTypeDisplayString}.Integer, {invariantCultureExpression})";
}
break;
case StringParsableTypeKind.Float:
{
- expressionForParsedValue = $"{typeDisplayString}.{Identifier.Parse}({Identifier.stringValue}, {numberStylesTypeDisplayString}.Float, {invariantCultureExpression})";
+ parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {numberStylesTypeDisplayString}.Float, {invariantCultureExpression})";
}
break;
case StringParsableTypeKind.Parse:
{
- expressionForParsedValue = $"{typeDisplayString}.{Identifier.Parse}({Identifier.stringValue})";
+ parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value})";
}
break;
case StringParsableTypeKind.ParseInvariant:
{
- expressionForParsedValue = $"{typeDisplayString}.{Identifier.Parse}({Identifier.stringValue}, {invariantCultureExpression})"; ;
+ parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {invariantCultureExpression})"; ;
}
break;
case StringParsableTypeKind.CultureInfo:
{
- expressionForParsedValue = $"{cultureInfoTypeDisplayString}.GetCultureInfo({Identifier.stringValue})";
+ parsedValueExpr = $"{cultureInfoTypeDisplayString}.GetCultureInfo({Identifier.value})";
}
break;
case StringParsableTypeKind.Uri:
{
- expressionForParsedValue = $"new Uri({Identifier.stringValue}, UriKind.RelativeOrAbsolute)";
+ parsedValueExpr = $"new Uri({Identifier.value}, UriKind.RelativeOrAbsolute)";
}
break;
default:
@@ -521,11 +594,11 @@ private void EmitPrimitiveParseMethod(ParsableFromStringSpec type)
}
_writer.WriteBlock($$"""
- public static {{typeDisplayString}} {{type.ParseMethodName}}(string {{Identifier.stringValue}}, Func {{Identifier.getPath}})
+ public static {{typeDisplayString}} {{type.ParseMethodName}}(string {{Identifier.value}}, Func {{Identifier.getPath}})
{
try
{
- return {{expressionForParsedValue}};
+ return {{parsedValueExpr}};
""");
string exceptionArg1 = string.Format(ExceptionMessages.FailedBinding, $"{{{Identifier.getPath}()}}", $"{{typeof({typeDisplayString})}}");
@@ -540,69 +613,19 @@ private void EmitPrimitiveParseMethod(ParsableFromStringSpec type)
""");
}
- private void EmitBindCoreImpl(TypeSpec type)
- {
- switch (type.SpecKind)
- {
- case TypeSpecKind.Enumerable:
- case TypeSpecKind.Dictionary:
- case TypeSpecKind.Object:
- {
- Debug.Assert(type.CanInitialize);
- EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType);
- EmitBindCoreImplForComplexType(type);
- }
- break;
- case TypeSpecKind.Nullable:
- {
- EmitBindCoreImpl(((NullableSpec)type).UnderlyingType);
- }
- break;
- case TypeSpecKind.IConfigurationSection:
- {
- EmitCastToIConfigurationSection();
- _writer.WriteLine($"{Identifier.obj} = {Identifier.section};");
- }
- break;
- default:
- Debug.Fail("Invalid type kind", type.SpecKind.ToString());
- break;
- }
- }
-
- private void EmitBindCoreImplForComplexType(TypeSpec type)
- {
- if (type.InitializationStrategy is InitializationStrategy.Array)
- {
- EmitPopulationImplForArray((EnumerableSpec)type);
- }
- else if (type is EnumerableSpec enumerable)
- {
- EmitPopulationImplForEnumerableWithAdd(enumerable);
- }
- else if (type is DictionarySpec dictionary)
- {
- EmitBindCoreImplForDictionary(dictionary);
- }
- else
- {
- EmitBindCoreImplForObject((ObjectSpec)type);
- }
- }
-
private void EmitPopulationImplForArray(EnumerableSpec type)
{
EnumerableSpec concreteType = (EnumerableSpec)type.ConcreteType;
- // Create, bind, and add elements to temp list.
- string tempVarName = GetIncrementalVarName(Identifier.temp);
- EmitBindCoreCall(concreteType, tempVarName, Identifier.configuration, InitializationKind.Declaration);
+ // Create list and bind elements.
+ string tempIdentifier = GetIncrementalIdentifier(Identifier.temp);
+ EmitBindCoreCall(concreteType, tempIdentifier, Identifier.configuration, InitializationKind.Declaration);
- // Resize array and copy additional elements.
+ // Resize array and add binded elements.
_writer.WriteBlock($$"""
{{Identifier.Int32}} {{Identifier.originalCount}} = {{Identifier.obj}}.{{Identifier.Length}};
- {{Identifier.Array}}.{{Identifier.Resize}}(ref {{Identifier.obj}}, {{Identifier.originalCount}} + {{tempVarName}}.{{Identifier.Count}});
- {{tempVarName}}.{{Identifier.CopyTo}}({{Identifier.obj}}, {{Identifier.originalCount}});
+ {{Identifier.Array}}.{{Identifier.Resize}}(ref {{Identifier.obj}}, {{Identifier.originalCount}} + {{tempIdentifier}}.{{Identifier.Count}});
+ {{tempIdentifier}}.{{Identifier.CopyTo}}({{Identifier.obj}}, {{Identifier.originalCount}});
""");
}
@@ -610,7 +633,7 @@ private void EmitPopulationImplForEnumerableWithAdd(EnumerableSpec type)
{
EmitCollectionCastIfRequired(type, out string objIdentifier);
- _writer.WriteBlockStart($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())");
+ Emit_Foreach_Section_In_ConfigChildren_BlockHeader();
TypeSpec elementType = type.ElementType;
@@ -620,13 +643,14 @@ private void EmitPopulationImplForEnumerableWithAdd(EnumerableSpec type)
stringParsableType,
Expression.sectionValue,
Expression.sectionPath,
- (parsedValueExpr) => _writer.WriteLine($"{objIdentifier}.{Identifier.Add}({parsedValueExpr}!);"),
- isCollectionElement: true);
+ (parsedValueExpr) => _writer.WriteLine($"{objIdentifier}.{Identifier.Add}({parsedValueExpr});"),
+ checkForNullSectionValue: true,
+ useIncrementalStringValueIdentifier: false);
}
else
{
- EmitBindCoreCall(elementType, Identifier.element, Identifier.section, InitializationKind.Declaration);
- _writer.WriteLine($"{objIdentifier}.{Identifier.Add}({Identifier.element});");
+ EmitBindCoreCall(elementType, Identifier.value, Identifier.section, InitializationKind.Declaration);
+ _writer.WriteLine($"{objIdentifier}.{Identifier.Add}({Identifier.value});");
}
_writer.WriteBlockEnd();
@@ -636,17 +660,19 @@ private void EmitBindCoreImplForDictionary(DictionarySpec type)
{
EmitCollectionCastIfRequired(type, out string objIdentifier);
- _writer.WriteBlockStart($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())");
+ Emit_Foreach_Section_In_ConfigChildren_BlockHeader();
ParsableFromStringSpec keyType = type.KeyType;
TypeSpec elementType = type.ElementType;
// Parse key
EmitBindLogicFromString(
- keyType,
- Expression.sectionKey,
- Expression.sectionPath,
- Emit_BindAndAddLogic_ForElement);
+ keyType,
+ Expression.sectionKey,
+ Expression.sectionPath,
+ Emit_BindAndAddLogic_ForElement,
+ checkForNullSectionValue: false,
+ useIncrementalStringValueIdentifier: false);
void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr)
{
@@ -656,15 +682,15 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr)
stringParsableElementType,
Expression.sectionValue,
Expression.sectionPath,
- (parsedValueExpr) => _writer.WriteLine($"{objIdentifier}[{parsedKeyExpr}!] = {parsedValueExpr}!;"),
- isCollectionElement: true);
+ writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{objIdentifier}[{parsedKeyExpr}] = {parsedValueExpr};"),
+ checkForNullSectionValue: true,
+ useIncrementalStringValueIdentifier: false);
}
else // For complex types:
{
Debug.Assert(elementType.CanInitialize);
- parsedKeyExpr += "!";
- if (keyType.StringParsableTypeKind is not StringParsableTypeKind.ConfigValue)
+ if (keyType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue)
{
// Save value to local to avoid parsing twice - during look-up and during add.
_writer.WriteLine($"{keyType.MinimalDisplayString} {Identifier.key} = {parsedKeyExpr};");
@@ -685,7 +711,7 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr)
}
_writer.WriteBlockStart($"if (!({conditionToUseExistingElement}))");
- EmitObjectInit(elementType, Identifier.element, InitializationKind.SimpleAssignment);
+ EmitObjectInit(elementType, Identifier.element, InitializationKind.SimpleAssignment, Identifier.section);
_writer.WriteBlockEnd();
if (elementType is CollectionSpec { InitializationStrategy: InitializationStrategy.ParameterizedConstructor or InitializationStrategy.ToEnumerableMethod } collectionSpec)
@@ -716,175 +742,152 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr)
private void EmitBindCoreImplForObject(ObjectSpec type)
{
- if (type.Properties.Count == 0)
- {
- return;
- }
+ Debug.Assert(type.NeedsMemberBinding);
- string listOfStringDisplayName = "List";
- _writer.WriteLine($"{listOfStringDisplayName}? {Identifier.temp} = null;");
-
- _writer.WriteBlockStart($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())");
- _writer.WriteBlockStart($"switch ({Expression.sectionKey})");
+ string keyCacheFieldName = GetConfigKeyCacheFieldName(type);
+ string validateMethodCallExpr = $"{Identifier.ValidateConfigurationKeys}(typeof({type.MinimalDisplayString}), {keyCacheFieldName}, {Identifier.configuration}, {Identifier.binderOptions});";
+ _writer.WriteLine(validateMethodCallExpr);
foreach (PropertySpec property in type.Properties.Values)
{
- _writer.WriteLine($@"case ""{property.ConfigurationKeyName}"":");
- _writer.Indentation++;
- _writer.WriteBlockStart();
-
- bool success = true;
- if (property.ShouldBind())
+ bool noSetter_And_IsReadonly = !property.CanSet && property.Type is CollectionSpec { InitializationStrategy: InitializationStrategy.ParameterizedConstructor };
+ if (property.ShouldBind() && !noSetter_And_IsReadonly)
{
- success = EmitBindCoreImplForProperty(property, property.Type, parentType: type);
+ string containingTypeRef = property.IsStatic ? type.MinimalDisplayString : Identifier.obj;
+ EmitBindImplForMember(
+ property,
+ memberAccessExpr: $"{containingTypeRef}.{property.Name}",
+ GetSectionPathFromConfigurationExpression(property.ConfigurationKeyName),
+ canSet: property.CanSet);
}
+ }
+ }
- _writer.WriteBlockEnd();
+ private bool EmitBindImplForMember(
+ MemberSpec member,
+ string memberAccessExpr,
+ string sectionPathExpr,
+ bool canSet)
+ {
+ TypeSpec effectiveMemberType = member.Type.EffectiveType;
- if (success)
+ if (effectiveMemberType is ParsableFromStringSpec stringParsableType)
+ {
+ if (canSet)
{
- _writer.WriteLine("break;");
- }
+ bool checkForNullSectionValue = member is ParameterSpec
+ ? true
+ : stringParsableType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue;
- _writer.Indentation--;
- }
-
- EmitSwitchDefault($$"""
- if ({{Identifier.binderOptions}}?.ErrorOnUnknownConfiguration == true)
- {
- ({{Identifier.temp}} ??= new {{listOfStringDisplayName}}()).Add($"'{{{Expression.sectionKey}}}'");
- }
- """);
+ string nullBangExpr = checkForNullSectionValue ? string.Empty : "!";
- // End switch on config child key.
- _writer.WriteBlockEnd();
+ EmitBlankLineIfRequired();
+ EmitBindLogicFromString(
+ stringParsableType,
+ $@"{Identifier.configuration}[""{member.ConfigurationKeyName}""]",
+ sectionPathExpr,
+ writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{memberAccessExpr} = {parsedValueExpr}{nullBangExpr};"),
+ checkForNullSectionValue,
+ useIncrementalStringValueIdentifier: true);
+ }
- // End foreach on config.GetChildren().
- _writer.WriteBlockEnd();
+ return true;
+ }
- _writer.WriteBlankLine();
+ string sectionParseExpr = $"{GetSectionFromConfigurationExpression(member.ConfigurationKeyName)}";
- string exceptionMessage = string.Format(ExceptionMessages.MissingConfig, Identifier.ErrorOnUnknownConfiguration, Identifier.BinderOptions, $"{{typeof({type.MinimalDisplayString})}}", $@"{{string.Join("", "", {Identifier.temp})}}");
- _writer.WriteBlock($$"""
- if ({{Identifier.temp}} is not null)
- {
- throw new InvalidOperationException($"{{exceptionMessage}}");
- }
- """);
+ EmitBlankLineIfRequired();
- }
+ if (effectiveMemberType.SpecKind is TypeSpecKind.IConfigurationSection)
+ {
+ _writer.WriteLine($"{memberAccessExpr} = {sectionParseExpr};");
+ return true;
+ }
- private bool EmitBindCoreImplForProperty(PropertySpec property, TypeSpec propertyType, TypeSpec parentType)
- {
- string configurationKeyName = property.ConfigurationKeyName;
- string propertyParentReference = property.IsStatic ? parentType.MinimalDisplayString : Identifier.obj;
- string expressionForPropertyAccess = $"{propertyParentReference}.{property.Name}";
- string expressionForConfigValueIndexer = $@"{Identifier.configuration}[""{configurationKeyName}""]";
+ string sectionValidationCall = $"{Identifier.AsConfigWithChildren}({sectionParseExpr})";
+ string sectionIdentifier = GetIncrementalIdentifier(Identifier.section);
- bool canSet = property.CanSet;
+ _writer.WriteBlockStart($"if ({sectionValidationCall} is {Identifier.IConfigurationSection} {sectionIdentifier})");
- switch (propertyType.SpecKind)
+ bool success = !EmitInitException(effectiveMemberType);
+ if (success)
{
- case TypeSpecKind.ParsableFromString:
- {
- if (canSet && propertyType is ParsableFromStringSpec stringParsableType)
- {
- EmitBindLogicFromString(
- stringParsableType,
- expressionForConfigValueIndexer,
- Expression.sectionPath,
- (parsedValueExpr) => _writer.WriteLine($"{expressionForPropertyAccess} = {parsedValueExpr}!;"));
- }
- }
- break;
- case TypeSpecKind.IConfigurationSection:
- {
- _writer.WriteLine($"{expressionForPropertyAccess} = {Identifier.section};");
- }
- break;
- case TypeSpecKind.Nullable:
- {
- TypeSpec underlyingType = ((NullableSpec)propertyType).UnderlyingType;
- EmitBindCoreImplForProperty(property, underlyingType, parentType);
- }
- break;
- default:
- {
- if (EmitInitException(propertyType))
- {
- return false;
- }
-
- EmitBindCoreCallForProperty(property, propertyType, expressionForPropertyAccess);
- }
- break;
+ EmitBindCoreCallForMember(member, memberAccessExpr, sectionIdentifier, canSet);
}
- return true;
+ _writer.WriteBlockEnd();
+ return success;
}
- private void EmitBindCoreCallForProperty(PropertySpec property, TypeSpec effectivePropertyType, string expressionForPropertyAccess)
+ private void EmitBindCoreCallForMember(
+ MemberSpec member,
+ string memberAccessExpr,
+ string configArgExpr,
+ bool canSet)
{
- _writer.WriteBlockStart($"if ({Identifier.HasChildren}({Identifier.section}))");
- bool canGet = property.CanGet;
- bool canSet = property.CanSet;
- string effectivePropertyTypeDisplayString = effectivePropertyType.MinimalDisplayString;
+ TypeSpec memberType = member.Type;
+ TypeSpec effectiveMemberType = memberType.EffectiveType;
+ string effectiveMemberTypeDisplayString = effectiveMemberType.MinimalDisplayString;
+ bool canGet = member.CanGet;
- string tempVarName = GetIncrementalVarName(Identifier.temp);
- if (effectivePropertyType.IsValueType)
+ string tempIdentifier = GetIncrementalIdentifier(Identifier.temp);
+ InitializationKind initKind;
+ string targetObjAccessExpr;
+
+ if (effectiveMemberType.IsValueType)
{
- if (canSet)
+ if (!canSet)
{
- if (canGet)
- {
- TypeSpec actualPropertyType = property.Type;
- if (actualPropertyType.SpecKind is TypeSpecKind.Nullable)
- {
- string nullableTempVarName = GetIncrementalVarName(Identifier.temp);
+ return;
+ }
- _writer.WriteLine($"{actualPropertyType.MinimalDisplayString} {nullableTempVarName} = {expressionForPropertyAccess};");
+ Debug.Assert(canSet);
+ initKind = InitializationKind.None;
- _writer.WriteLine(
- $"{effectivePropertyTypeDisplayString} {tempVarName} = {nullableTempVarName}.{Identifier.HasValue} ? {nullableTempVarName}.{Identifier.Value} : new {effectivePropertyTypeDisplayString}();");
- }
- else
- {
- _writer.WriteLine($"{effectivePropertyTypeDisplayString} {tempVarName} = {expressionForPropertyAccess};");
- }
- }
- else
- {
- EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.Declaration);
- }
-
- _writer.WriteLine($@"{Identifier.BindCore}({Identifier.section}, ref {tempVarName}, {Identifier.binderOptions});");
- _writer.WriteLine($"{expressionForPropertyAccess} = {tempVarName};");
- }
- }
- else
- {
- if (canGet)
+ if (memberType.SpecKind is TypeSpecKind.Nullable)
{
- _writer.WriteLine($"{effectivePropertyTypeDisplayString} {tempVarName} = {expressionForPropertyAccess};");
- EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.AssignmentWithNullCheck);
- _writer.WriteLine($@"{Identifier.BindCore}({Identifier.section}, ref {tempVarName}, {Identifier.binderOptions});");
+ string nullableTempIdentifier = GetIncrementalIdentifier(Identifier.temp);
- if (canSet)
- {
- _writer.WriteLine($"{expressionForPropertyAccess} = {tempVarName};");
- }
+ _writer.WriteLine($"{memberType.MinimalDisplayString} {nullableTempIdentifier} = {memberAccessExpr};");
+
+ _writer.WriteLine(
+ $"{effectiveMemberTypeDisplayString} {tempIdentifier} = {nullableTempIdentifier}.{Identifier.HasValue} ? {nullableTempIdentifier}.{Identifier.Value} : new {effectiveMemberTypeDisplayString}();");
}
else
{
- Debug.Assert(canSet);
- EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.Declaration);
- _writer.WriteLine($@"{Identifier.BindCore}({Identifier.section}, ref {tempVarName}, {Identifier.binderOptions});");
- _writer.WriteLine($"{expressionForPropertyAccess} = {tempVarName};");
+ _writer.WriteLine($"{effectiveMemberTypeDisplayString} {tempIdentifier} = {memberAccessExpr};");
}
- }
- _writer.WriteBlockEnd();
+ targetObjAccessExpr = tempIdentifier;
+ }
+ else if (canGet)
+ {
+ targetObjAccessExpr = memberAccessExpr;
+ initKind = InitializationKind.AssignmentWithNullCheck;
+ }
+ else
+ {
+ targetObjAccessExpr = memberAccessExpr;
+ initKind = InitializationKind.SimpleAssignment;
+ }
+
+ Action? writeOnSuccess = !canSet
+ ? null
+ : bindedValueIdentifier =>
+ {
+ if (memberAccessExpr != bindedValueIdentifier)
+ {
+ _writer.WriteLine($"{memberAccessExpr} = {bindedValueIdentifier};");
+ }
+ };
+
+ EmitBindCoreCall(
+ effectiveMemberType,
+ targetObjAccessExpr,
+ configArgExpr,
+ initKind,
+ writeOnSuccess);
}
private void EmitCollectionCastIfRequired(CollectionSpec type, out string objIdentifier)
@@ -903,22 +906,21 @@ private void EmitCollectionCastIfRequired(CollectionSpec type, out string objIde
}
}
- private void EmitSwitchDefault(string caseLogic, bool addBreak = true)
- {
- _writer.WriteLine("default:");
- _writer.Indentation++;
- _writer.WriteBlockStart();
- _writer.WriteBlock(caseLogic);
- _writer.WriteBlockEnd();
+ private void Emit_Foreach_Section_In_ConfigChildren_BlockHeader() =>
+ _writer.WriteBlockStart($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())");
- if (addBreak)
- {
- _writer.WriteLine("break;");
- }
+ private static string GetSectionPathFromConfigurationExpression(string configurationKeyName)
+ => $@"{GetSectionFromConfigurationExpression(configurationKeyName)}.{Identifier.Path}";
- _writer.Indentation--;
+ private static string GetSectionFromConfigurationExpression(string configurationKeyName, bool addQuotes = true)
+ {
+ string argExpr = addQuotes ? $@"""{configurationKeyName}""" : configurationKeyName;
+ return $@"{Expression.configurationGetSection}({argExpr})";
}
+ private static string GetConfigKeyCacheFieldName(ObjectSpec type) =>
+ $"s_configKeys_{type.DisplayStringWithoutSpecialCharacters}";
+
private void Emit_NotSupportedException_TypeNotDetectedAsInput() =>
_writer.WriteLine(@$"throw new global::System.NotSupportedException($""{string.Format(ExceptionMessages.TypeNotDetectedAsInput, "{type}")}"");");
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs
index dd4ba3907e3372..7325c292a0036a 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Reflection;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
@@ -9,6 +10,8 @@ public sealed partial class ConfigurationBindingGenerator
{
private sealed partial class Emitter
{
+ private static readonly AssemblyName s_assemblyName = typeof(Emitter).Assembly.GetName();
+
private enum InitializationKind
{
None = 0,
@@ -18,6 +21,7 @@ private enum InitializationKind
}
private static class Expression
{
+ public const string configurationGetSection = "configuration.GetSection";
public const string sectionKey = "section.Key";
public const string sectionPath = "section.Path";
public const string sectionValue = "section.Value";
@@ -30,7 +34,7 @@ private static class FullyQualifiedDisplayString
public const string ActionOfBinderOptions = $"global::System.Action";
public const string AddSingleton = $"{ServiceCollectionServiceExtensions}.AddSingleton";
public const string ConfigurationChangeTokenSource = "global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource";
- public const string CoreBindingHelper = $"global::{ConfigurationBindingGenerator.ProjectName}.{Identifier.CoreBindingHelper}";
+ public const string CoreBindingHelper = $"global::{ProjectName}.{Identifier.CoreBindingHelper}";
public const string IConfiguration = "global::Microsoft.Extensions.Configuration.IConfiguration";
public const string IConfigurationSection = IConfiguration + "Section";
public const string IOptionsChangeTokenSource = "global::Microsoft.Extensions.Options.IOptionsChangeTokenSource";
@@ -45,6 +49,9 @@ private static class FullyQualifiedDisplayString
private static class MinimalDisplayString
{
public const string NullableActionOfBinderOptions = "Action?";
+ public const string HashSetOfString = "HashSet";
+ public const string LazyHashSetOfString = "Lazy>";
+ public const string ListOfString = "List";
}
private static class Identifier
@@ -64,18 +71,19 @@ private static class Identifier
public const string optionsBuilder = nameof(optionsBuilder);
public const string originalCount = nameof(originalCount);
public const string section = nameof(section);
+ public const string sectionKey = nameof(sectionKey);
public const string services = nameof(services);
- public const string stringValue = nameof(stringValue);
public const string temp = nameof(temp);
public const string type = nameof(type);
+ public const string validateKeys = nameof(validateKeys);
+ public const string value = nameof(value);
public const string Add = nameof(Add);
public const string AddSingleton = nameof(AddSingleton);
public const string Any = nameof(Any);
public const string Array = nameof(Array);
+ public const string AsConfigWithChildren = nameof(AsConfigWithChildren);
public const string Bind = nameof(Bind);
- public const string BindCore = nameof(BindCore);
- public const string BindCoreUntyped = nameof(BindCoreUntyped);
public const string BinderOptions = nameof(BinderOptions);
public const string Configure = nameof(Configure);
public const string CopyTo = nameof(CopyTo);
@@ -91,12 +99,9 @@ private static class Identifier
public const string GeneratedServiceCollectionBinder = nameof(GeneratedServiceCollectionBinder);
public const string Get = nameof(Get);
public const string GetBinderOptions = nameof(GetBinderOptions);
- public const string GetCore = nameof(GetCore);
public const string GetChildren = nameof(GetChildren);
public const string GetSection = nameof(GetSection);
public const string GetValue = nameof(GetValue);
- public const string GetValueCore = nameof(GetValueCore);
- public const string HasChildren = nameof(HasChildren);
public const string HasConfig = nameof(HasConfig);
public const string HasValueOrChildren = nameof(HasValueOrChildren);
public const string HasValue = nameof(HasValue);
@@ -115,6 +120,7 @@ private static class Identifier
public const string TryGetValue = nameof(TryGetValue);
public const string TryParse = nameof(TryParse);
public const string Uri = nameof(Uri);
+ public const string ValidateConfigurationKeys = nameof(ValidateConfigurationKeys);
public const string Value = nameof(Value);
}
@@ -125,12 +131,12 @@ private bool ShouldEmitBinders() =>
private void EmitBlankLineIfRequired()
{
- if (_precedingBlockExists)
+ if (_emitBlankLineBeforeNextStatement)
{
_writer.WriteBlankLine();
}
- _precedingBlockExists = true;
+ _emitBlankLineBeforeNextStatement = true;
}
private void EmitCheckForNullArgument_WithBlankLine_IfRequired(bool isValueType)
@@ -170,9 +176,31 @@ private bool EmitInitException(TypeSpec type)
return false;
}
+ private void EmitRootBindingClassBlockStart(string className)
+ {
+ EmitBlankLineIfRequired();
+ _writer.WriteBlock($$"""
+ /// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+ {{GetGeneratedCodeAttributeSrc()}}
+ internal static class {{className}}
+ {
+ """);
+
+ _emitBlankLineBeforeNextStatement = false;
+ }
+
+ private string GetGeneratedCodeAttributeSrc()
+ {
+ string attributeRefExpr = _useFullyQualifiedNames ? $"global::System.CodeDom.Compiler.GeneratedCodeAttribute" : "GeneratedCode";
+ return $@"[{attributeRefExpr}(""{s_assemblyName.Name}"", ""{s_assemblyName.Version}"")]";
+ }
+
private string GetInitException(string message) => $@"throw new {GetInvalidOperationDisplayName()}(""{message}"")";
- private string GetIncrementalVarName(string prefix) => $"{prefix}{_parseValueCount++}";
+ private string GetIncrementalIdentifier(string prefix) => $"{prefix}{_valueSuffixIndex++}";
+
+ private string GetInitalizeMethodDisplayString(ObjectSpec type) =>
+ GetHelperMethodDisplayString($"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.DisplayStringWithoutSpecialCharacters}");
private string GetTypeDisplayString(TypeSpec type) => _useFullyQualifiedNames ? type.FullyQualifiedDisplayString : type.MinimalDisplayString;
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs
index e39e65e6125543..0055b2bf127c02 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Microsoft.CodeAnalysis;
-
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
public sealed partial class ConfigurationBindingGenerator
@@ -18,10 +16,7 @@ private void EmitBinder_Extensions_OptionsBuilder()
return;
}
- EmitBlankLineIfRequired();
- _writer.WriteLine("/// Generated helper providing an AOT and linking compatible implementation for configuration binding.");
- _writer.WriteBlockStart($"internal static class {Identifier.GeneratedOptionsBuilderBinder}");
- _precedingBlockExists = false;
+ EmitRootBindingClassBlockStart(Identifier.GeneratedOptionsBuilderBinder);
EmitBindMethods_Extensions_OptionsBuilder();
EmitBindConfigurationMethod();
@@ -44,7 +39,6 @@ private void EmitBindMethods_Extensions_OptionsBuilder()
EmitMethodBlockStart("Bind", paramList, documentation);
_writer.WriteLine($"return global::{Identifier.GeneratedOptionsBuilderBinder}.Bind({Identifier.optionsBuilder}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
_writer.WriteBlockEnd();
- _writer.WriteBlankLine();
}
EmitMethodBlockStart(
@@ -81,7 +75,7 @@ private void EmitBindConfigurationMethod()
_writer.WriteBlock($$"""
{{FullyQualifiedDisplayString.IConfiguration}} {{Identifier.section}} = string.Equals(string.Empty, {{Identifier.configSectionPath}}, global::System.StringComparison.OrdinalIgnoreCase) ? {{Identifier.configuration}} : {{Identifier.configuration}}.{{Identifier.GetSection}}({{Identifier.configSectionPath}});
- {{FullyQualifiedDisplayString.CoreBindingHelper}}.{{Identifier.BindCoreUntyped}}({{Identifier.section}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}});
+ {{FullyQualifiedDisplayString.CoreBindingHelper}}.{{nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}}({{Identifier.section}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}});
""");
_writer.WriteBlockEnd(");");
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs
index 8dd6a00b786968..5b4edc93b8a51a 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Microsoft.CodeAnalysis;
-
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
public sealed partial class ConfigurationBindingGenerator
@@ -18,11 +16,7 @@ private void EmitBinder_Extensions_ServiceCollection()
return;
}
- EmitBlankLineIfRequired();
-
- _writer.WriteLine("/// Generated helper providing an AOT and linking compatible implementation for configuration binding.");
- _writer.WriteBlockStart($"internal static class {Identifier.GeneratedServiceCollectionBinder}");
- _precedingBlockExists = false;
+ EmitRootBindingClassBlockStart(Identifier.GeneratedServiceCollectionBinder);
const string defaultNameExpr = "string.Empty";
const string configureMethodString = $"global::{Identifier.GeneratedServiceCollectionBinder}.{Identifier.Configure}";
@@ -52,7 +46,7 @@ private void EmitBinder_Extensions_ServiceCollection()
}
string optionsNamespaceName = "global::Microsoft.Extensions.Options";
- string bindCoreUntypedDisplayString = GetHelperMethodDisplayString(Identifier.BindCoreUntyped);
+ string bindCoreUntypedDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped));
EmitBlockStart(paramList: $"string? {Identifier.name}, " + configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}");
@@ -67,7 +61,7 @@ private void EmitBinder_Extensions_ServiceCollection()
""");
_writer.WriteBlockEnd();
- _precedingBlockExists = true;
+ _emitBlankLineBeforeNextStatement = true;
}
private void EmitBlockStart(string paramList)
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs
index 8f2c37af6c70a5..f49deca75971cd 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs
@@ -14,6 +14,7 @@ public enum MethodsToGen_CoreBindingHelper
GetCore = 0x4,
GetValueCore = 0x8,
Initialize = 0x10,
+ AsConfigWithChildren = 0x20,
}
///
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs
index 7db7c965b153e2..26287798463ea9 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs
@@ -111,7 +111,6 @@ private void RegisterBindInvocation(BinderInvocation invocation)
_sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload;
typeSpecs.Add(typeSpec);
- RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, typeSpec);
}
static ITypeSymbol? ResolveType(IOperation conversionOperation) =>
@@ -124,6 +123,7 @@ private void RegisterBindInvocation(BinderInvocation invocation)
IMethodReferenceOperation m when m.Method.MethodKind == MethodKind.Constructor => m.Method.ContainingType,
IMethodReferenceOperation m => m.Method.ReturnType,
IAnonymousFunctionOperation f => f.Symbol.ReturnType,
+ IParameterReferenceOperation p => p.Parameter.Type,
_ => null
};
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/SourceWriter.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/SourceWriter.cs
index 4787c6999b9665..f610209874c7d0 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/SourceWriter.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/SourceWriter.cs
@@ -14,22 +14,6 @@ internal sealed class SourceWriter
private static readonly char[] s_newLine = Environment.NewLine.ToCharArray();
private int _indentation;
-
- public int Indentation
- {
- get => _indentation;
- set
- {
- if (value < 0)
- {
- Throw();
- static void Throw() => throw new ArgumentOutOfRangeException(nameof(value));
- }
-
- _indentation = value;
- }
- }
-
public void WriteBlockStart(string? declaration = null)
{
if (declaration is not null)
@@ -37,19 +21,19 @@ public void WriteBlockStart(string? declaration = null)
WriteLine(declaration);
}
WriteLine("{");
- Indentation++;
+ _indentation++;
}
public void WriteBlockEnd(string? extra = null)
{
- Indentation--;
- Debug.Assert(Indentation > -1);
+ _indentation--;
+ Debug.Assert(_indentation > -1);
WriteLine($"}}{extra}");
}
public void WriteLine(string source)
{
- _sb.Append(' ', 4 * Indentation);
+ _sb.Append(' ', 4 * _indentation);
_sb.AppendLine(source);
}
@@ -86,7 +70,7 @@ public void WriteBlock(string source)
public SourceText ToSourceText()
{
- Debug.Assert(Indentation == 0 && _sb.Length > 0);
+ Debug.Assert(_indentation == 0 && _sb.Length > 0);
return SourceText.From(_sb.ToString(), Encoding.UTF8);
}
@@ -123,7 +107,7 @@ private static ReadOnlySpan GetNextLine(ref ReadOnlySpan remainingTe
private unsafe void WriteLine(ReadOnlySpan source)
{
- _sb.Append(' ', 4 * Indentation);
+ _sb.Append(' ', 4 * _indentation);
fixed (char* ptr = source)
{
_sb.Append(ptr, source.Length);
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj
index fc2d2bae89b2e9..f1c71a5575c23e 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj
@@ -41,6 +41,7 @@
+
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/CollectionSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/CollectionSpec.cs
index 7c90701fbda035..280ecc4c536482 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/CollectionSpec.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/CollectionSpec.cs
@@ -17,11 +17,13 @@ public CollectionSpec(ITypeSymbol type) : base(type) { }
public required CollectionPopulationStrategy PopulationStrategy { get; init; }
- public override bool CanInitialize => ConcreteType?.CanInitialize ?? CanInitCompexType;
+ public override bool CanInitialize => ConcreteType?.CanInitialize ?? CanInitComplexObject();
public override required InitializationStrategy InitializationStrategy { get; set; }
public required string? ToEnumerableMethodCall { get; init; }
+
+ public sealed override bool NeedsMemberBinding => true;
}
internal sealed record EnumerableSpec : CollectionSpec
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/MemberSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/MemberSpec.cs
new file mode 100644
index 00000000000000..4bf674f597502a
--- /dev/null
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/MemberSpec.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
+{
+ internal abstract record MemberSpec
+ {
+ public MemberSpec(ISymbol member)
+ {
+ Debug.Assert(member is IPropertySymbol or IParameterSymbol);
+ Name = member.Name;
+ DefaultValueExpr = "default";
+ }
+
+ public string Name { get; }
+ public bool ErrorOnFailedBinding { get; protected set; }
+ public string DefaultValueExpr { get; protected set; }
+
+ public required TypeSpec Type { get; init; }
+ public required string ConfigurationKeyName { get; init; }
+
+ public abstract bool CanGet { get; }
+ public abstract bool CanSet { get; }
+ }
+}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/NullableSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/NullableSpec.cs
index c59e17e3c24240..9dcca27596e7ee 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/NullableSpec.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/NullableSpec.cs
@@ -1,23 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using Microsoft.CodeAnalysis;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal sealed record NullableSpec : TypeSpec
{
- public NullableSpec(ITypeSymbol type) : base(type) { }
+ private readonly TypeSpec _underlyingType;
- public override TypeSpecKind SpecKind => TypeSpecKind.Nullable;
-
- public required TypeSpec UnderlyingType { get; init; }
-
- public override string? InitExceptionMessage
+ public NullableSpec(ITypeSymbol type, TypeSpec underlyingType) : base(type)
{
- get => UnderlyingType.InitExceptionMessage;
- set => throw new InvalidOperationException();
+ _underlyingType = underlyingType;
}
+
+ public override TypeSpecKind SpecKind => TypeSpecKind.Nullable;
+
+ public override TypeSpec EffectiveType => _underlyingType;
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs
index 4dbfc4a1df9bf7..1696ee099fe469 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs
@@ -3,27 +3,31 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Microsoft.CodeAnalysis;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal sealed record ObjectSpec : TypeSpec
{
- public ObjectSpec(INamedTypeSymbol type) : base(type)
- {
- InitializeMethodDisplayString = $"Initialize{type.Name.Replace(".", string.Empty).Replace("<", string.Empty).Replace(">", string.Empty)}";
- }
+ public ObjectSpec(INamedTypeSymbol type) : base(type) { }
public override TypeSpecKind SpecKind => TypeSpecKind.Object;
public override InitializationStrategy InitializationStrategy { get; set; }
- public override bool CanInitialize => CanInitCompexType;
+ public override bool CanInitialize => CanInitComplexObject();
public Dictionary Properties { get; } = new(StringComparer.OrdinalIgnoreCase);
public List ConstructorParameters { get; } = new();
- public string? InitializeMethodDisplayString { get; }
+ private string _displayStringWithoutSpecialCharacters;
+ public string DisplayStringWithoutSpecialCharacters =>
+ _displayStringWithoutSpecialCharacters ??= $"{MinimalDisplayString.Replace(".", string.Empty).Replace("<", string.Empty).Replace(">", string.Empty)}";
+
+ public override bool NeedsMemberBinding => CanInitialize &&
+ Properties.Values.Count > 0 &&
+ Properties.Values.Any(p => p.ShouldBind());
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParameterSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParameterSpec.cs
index a62f6080537ba2..9b5e4360c11169 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParameterSpec.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParameterSpec.cs
@@ -6,31 +6,30 @@
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
- internal sealed record ParameterSpec
+ internal sealed record ParameterSpec : MemberSpec
{
- public ParameterSpec(IParameterSymbol parameter)
+ public ParameterSpec(IParameterSymbol parameter) : base(parameter)
{
- Name = parameter.Name;
RefKind = parameter.RefKind;
- HasExplicitDefaultValue = parameter.HasExplicitDefaultValue;
- if (HasExplicitDefaultValue)
+ if (parameter.HasExplicitDefaultValue)
{
string formatted = SymbolDisplay.FormatPrimitive(parameter.ExplicitDefaultValue, quoteStrings: true, useHexadecimalNumbers: false);
- DefaultValue = formatted is "null" ? "default!" : formatted;
+ if (formatted is not "null")
+ {
+ DefaultValueExpr = formatted;
+ }
+ }
+ else
+ {
+ ErrorOnFailedBinding = true;
}
}
- public required TypeSpec Type { get; init; }
-
- public string Name { get; }
-
- public required string ConfigurationKeyName { get; init; }
-
public RefKind RefKind { get; }
- public bool HasExplicitDefaultValue { get; init; }
+ public override bool CanGet => false;
- public string DefaultValue { get; } = "default!";
+ public override bool CanSet => true;
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParsableFromStringSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParsableFromStringSpec.cs
index 7f910a05c5221c..6b5bb5b61ea371 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParsableFromStringSpec.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParsableFromStringSpec.cs
@@ -19,7 +19,7 @@ public string ParseMethodName
{
get
{
- Debug.Assert(StringParsableTypeKind is not StringParsableTypeKind.ConfigValue);
+ Debug.Assert(StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue);
_parseMethodName ??= StringParsableTypeKind is StringParsableTypeKind.ByteArray
? "ParseByteArray"
@@ -34,7 +34,11 @@ public string ParseMethodName
internal enum StringParsableTypeKind
{
None = 0,
- ConfigValue = 1,
+
+ ///
+ /// Declared types that can be assigned directly from IConfigurationSection.Value, i.e. string and tyepof(object).
+ ///
+ AssignFromSectionValue = 1,
Enum = 2,
ByteArray = 3,
Integer = 4,
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/PropertySpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/PropertySpec.cs
index 35d79a296eda73..584e8d570b8a9f 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/PropertySpec.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/PropertySpec.cs
@@ -5,43 +5,30 @@
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
- internal sealed record PropertySpec
+ internal sealed record PropertySpec : MemberSpec
{
- public PropertySpec(IPropertySymbol property)
+ public PropertySpec(IPropertySymbol property) : base(property)
{
- Name = property.Name;
- IsStatic = property.IsStatic;
+ IMethodSymbol? setMethod = property.SetMethod;
+ bool setterIsPublic = setMethod?.DeclaredAccessibility is Accessibility.Public;
+ bool isInitOnly = setMethod?.IsInitOnly is true;
- bool setterIsPublic = property.SetMethod?.DeclaredAccessibility is Accessibility.Public;
- IsInitOnly = property.SetMethod?.IsInitOnly == true;
- IsRequired = property.IsRequired;
- SetOnInit = setterIsPublic && (IsInitOnly || IsRequired);
- CanSet = setterIsPublic && !IsInitOnly;
+ IsStatic = property.IsStatic;
+ SetOnInit = setterIsPublic && (property.IsRequired || isInitOnly);
+ CanSet = setterIsPublic && !isInitOnly;
CanGet = property.GetMethod?.DeclaredAccessibility is Accessibility.Public;
}
- public required TypeSpec Type { get; init; }
-
public ParameterSpec? MatchingCtorParam { get; set; }
- public string Name { get; }
-
public bool IsStatic { get; }
- public bool IsRequired { get; }
-
- public bool IsInitOnly { get; }
-
public bool SetOnInit { get; }
- public bool CanGet { get; }
-
- public bool CanSet { get; }
+ public override bool CanGet { get; }
- public required string ConfigurationKeyName { get; init; }
+ public override bool CanSet { get; }
- public bool ShouldBind() =>
- (CanGet || CanSet) &&
- !(!CanSet && (Type as CollectionSpec)?.InitializationStrategy is InitializationStrategy.ParameterizedConstructor);
+ public bool ShouldBind() => CanGet || CanSet;
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs
index 941320e7fa72be..88c4b24f57a5ea 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs
@@ -11,13 +11,17 @@ internal sealed record SourceGenerationSpec
public Dictionary> TypesForGen_ConfigurationBinder_BindMethods { get; } = new();
public HashSet PrimitivesForHelperGen { get; } = new();
- public HashSet TypeNamespaces { get; } = new() { "Microsoft.Extensions.Configuration", "System.Globalization" };
+ public HashSet TypeNamespaces { get; } = new()
+ {
+ "System",
+ "System.CodeDom.Compiler",
+ "System.Globalization",
+ "Microsoft.Extensions.Configuration",
+ };
public MethodsToGen_CoreBindingHelper MethodsToGen_CoreBindingHelper { get; set; }
public MethodsToGen_ConfigurationBinder MethodsToGen_ConfigurationBinder { get; set; }
public MethodsToGen_Extensions_OptionsBuilder MethodsToGen_OptionsBuilderExt { get; set; }
public MethodsToGen_Extensions_ServiceCollection MethodsToGen_ServiceCollectionExt { get; set; }
-
- public bool ShouldEmitHasChildren { get; set; }
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/TypeSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/TypeSpec.cs
index f53b92960230ac..6a6292b7ebd0b4 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/TypeSpec.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/TypeSpec.cs
@@ -20,6 +20,7 @@ public TypeSpec(ITypeSymbol type)
FullyQualifiedDisplayString = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
MinimalDisplayString = type.ToDisplayString(s_minimalDisplayFormat);
Name = Namespace + "." + MinimalDisplayString.Replace(".", "+");
+ IsInterface = type.TypeKind is TypeKind.Interface;
}
public string Name { get; }
@@ -40,12 +41,13 @@ public TypeSpec(ITypeSymbol type)
public virtual bool CanInitialize => true;
- ///
- /// Location in the input compilation we picked up a call to Bind, Get, or Configure.
- ///
- public required Location? Location { get; init; }
+ public virtual bool NeedsMemberBinding { get; }
- protected bool CanInitCompexType => InitializationStrategy is not InitializationStrategy.None && InitExceptionMessage is null;
+ public virtual TypeSpec EffectiveType => this;
+
+ public bool IsInterface { get; }
+
+ protected bool CanInitComplexObject() => InitializationStrategy is not InitializationStrategy.None && InitExceptionMessage is null;
}
internal enum TypeSpecKind
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 fadaa1c0e69f1b..fb51320941bef7 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
@@ -15,7 +15,7 @@ namespace Microsoft.Extensions
#endif
.Configuration.Binder.Tests
{
- public partial class ConfigurationBinderCollectionTests
+ public sealed partial class ConfigurationBinderCollectionTests : ConfigurationBinderTestsBase
{
[Fact]
public void GetList()
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 c2068feefb6d01..5e4855a55d2ff3 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
@@ -36,6 +36,15 @@ public class GenericOptions
public T Value { get; set; }
}
+ public record GenericOptionsRecord(T Value);
+
+ public class GenericOptionsWithParamCtor
+ {
+ public GenericOptionsWithParamCtor(T value) => Value = value;
+
+ public T Value { get; }
+ }
+
public class OptionsWithNesting
{
public NestedOptions Nested { 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 75377493a423bf..d9dfefecce0c76 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs
@@ -19,7 +19,17 @@ namespace Microsoft.Extensions
#endif
.Configuration.Binder.Tests
{
- public partial class ConfigurationBinderTests
+ public abstract class ConfigurationBinderTestsBase
+ {
+ public ConfigurationBinderTestsBase()
+ {
+#if LAUNCH_DEBUGGER
+if (!System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Launch(); }
+#endif
+ }
+ }
+
+ public sealed partial class ConfigurationBinderTests : ConfigurationBinderTestsBase
{
[Fact]
public void BindWithNestedTypesWithReadOnlyProperties()
@@ -1870,5 +1880,25 @@ public void BindRootStructIsNoOp()
Assert.Equal(0, obj.Int32);
Assert.False(obj.Boolean);
}
+
+ [Fact]
+ public void AllowsCaseInsensitiveMatch()
+ {
+ var configuration = TestHelpers.GetConfigurationFromJsonString("""
+ {
+ "vaLue": "MyString",
+ }
+ """);
+
+ GenericOptions obj = new();
+ configuration.Bind(obj);
+ Assert.Equal("MyString", obj.Value);
+
+ GenericOptionsRecord obj1 = configuration.Get>();
+ Assert.Equal("MyString", obj1.Value);
+
+ GenericOptionsWithParamCtor obj2 = configuration.Get>();
+ Assert.Equal("MyString", obj2.Value);
+ }
}
}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt
index 2965e8de36ca98..f0b2ffb49b4867 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Attempts to bind the configuration instance to a new instance of type T.
@@ -12,13 +14,17 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
+ private readonly static Lazy> s_configKeys_ProgramMyClassWithCustomCollections = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "CustomDictionary", "CustomList", "IReadOnlyList", "IReadOnlyDictionary" });
+
public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions)
{
if (configuration is null)
@@ -52,9 +58,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue2)
+ if (section.Value is string value)
{
- obj[section.Key!] = ParseInt(stringValue2, () => section.Path)!;
+ obj[section.Key] = ParseInt(value, () => section.Path);
}
}
}
@@ -68,9 +74,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue3)
+ if (section.Value is string value)
{
- obj.Add(stringValue3!);
+ obj.Add(value);
+ }
+ }
+ }
+
+ public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions)
+ {
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Value is string value)
+ {
+ obj.Add(ParseInt(value, () => section.Path));
+ }
+ }
+ }
+
+ public static void BindCore(IConfiguration configuration, ref ICollection obj, BinderOptions? binderOptions)
+ {
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Value is string value)
+ {
+ obj.Add(ParseInt(value, () => section.Path));
}
}
}
@@ -89,9 +127,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue4)
+ if (section.Value is string value)
+ {
+ temp.Add(ParseInt(value, () => section.Path));
+ }
+ }
+ }
+
+ public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions)
+ {
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Value is string value)
{
- temp.Add(ParseInt(stringValue4, () => section.Path)!);
+ obj[section.Key] = ParseInt(value, () => section.Path);
+ }
+ }
+ }
+
+ public static void BindCore(IConfiguration configuration, ref IDictionary obj, BinderOptions? binderOptions)
+ {
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Value is string value)
+ {
+ obj[section.Key] = ParseInt(value, () => section.Path);
}
}
}
@@ -110,9 +180,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue6)
+ if (section.Value is string value)
{
- temp[section.Key!] = ParseInt(stringValue6, () => section.Path)!;
+ temp[section.Key] = ParseInt(value, () => section.Path);
}
}
}
@@ -124,69 +194,58 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClassWithCustomCollections), s_configKeys_ProgramMyClassWithCustomCollections, configuration, binderOptions);
+
+ if (AsConfigWithChildren(configuration.GetSection("CustomDictionary")) is IConfigurationSection section1)
{
- switch (section.Key)
- {
- case "CustomDictionary":
- {
- if (HasChildren(section))
- {
- Program.CustomDictionary temp7 = obj.CustomDictionary;
- temp7 ??= new Program.CustomDictionary();
- BindCore(section, ref temp7, binderOptions);
- obj.CustomDictionary = temp7;
- }
- }
- break;
- case "CustomList":
- {
- if (HasChildren(section))
- {
- Program.CustomList temp8 = obj.CustomList;
- temp8 ??= new Program.CustomList();
- BindCore(section, ref temp8, binderOptions);
- obj.CustomList = temp8;
- }
- }
- break;
- case "IReadOnlyList":
- {
- if (HasChildren(section))
- {
- IReadOnlyList temp9 = obj.IReadOnlyList;
- temp9 = temp9 is null ? new List() : new List(temp9);
- BindCore(section, ref temp9, binderOptions);
- obj.IReadOnlyList = temp9;
- }
- }
- break;
- case "IReadOnlyDictionary":
- {
- if (HasChildren(section))
- {
- IReadOnlyDictionary temp10 = obj.IReadOnlyDictionary;
- temp10 = temp10 is null ? new Dictionary() : temp10.ToDictionary(pair => pair.Key, pair => pair.Value);
- BindCore(section, ref temp10, binderOptions);
- obj.IReadOnlyDictionary = temp10;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ Program.CustomDictionary temp3 = obj.CustomDictionary;
+ temp3 ??= new Program.CustomDictionary();
+ BindCore(section1, ref temp3, binderOptions);
+ obj.CustomDictionary = temp3;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("CustomList")) is IConfigurationSection section4)
+ {
+ Program.CustomList temp6 = obj.CustomList;
+ temp6 ??= new Program.CustomList();
+ BindCore(section4, ref temp6, binderOptions);
+ obj.CustomList = temp6;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("IReadOnlyList")) is IConfigurationSection section7)
+ {
+ IReadOnlyList temp9 = obj.IReadOnlyList;
+ temp9 = temp9 is null ? new List() : new List(temp9);
+ BindCore(section7, ref temp9, binderOptions);
+ obj.IReadOnlyList = temp9;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("IReadOnlyDictionary")) is IConfigurationSection section10)
+ {
+ IReadOnlyDictionary temp12 = obj.IReadOnlyDictionary;
+ temp12 = temp12 is null ? new Dictionary() : temp12.ToDictionary(pair => pair.Key, pair => pair.Value);
+ BindCore(section10, ref temp12, binderOptions);
+ obj.IReadOnlyDictionary = temp12;
}
+ }
- if (temp is not 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)
+ {
+ if (binderOptions?.ErrorOnUnknownConfiguration is true)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClassWithCustomCollections)}: {string.Join(", ", temp)}");
+ List? temp = null;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (!keys.Value.Contains(section.Key))
+ {
+ (temp ??= new List()).Add($"'{section.Key}'");
+ }
+ }
+ if (temp is not null)
+ {
+ throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}");
+ }
}
}
@@ -196,16 +255,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return true;
}
- return HasChildren(configuration);
+ return AsConfigWithChildren(configuration) is not null;
}
- public static bool HasChildren(IConfiguration configuration)
+ public static IConfiguration? AsConfigWithChildren(IConfiguration configuration)
{
- foreach (IConfigurationSection section in configuration.GetChildren())
+ foreach (IConfigurationSection _ in configuration.GetChildren())
{
- return true;
+ return configuration;
}
- return false;
+ return null;
}
public static BinderOptions? GetBinderOptions(Action? configureOptions)
@@ -223,11 +282,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return binderOptions;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt
index 92b7270ef2da7b..260a7ad627951e 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.
@@ -18,12 +20,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
+ private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
+
public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions)
{
if (obj is null)
@@ -33,9 +39,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue0)
+ if (section.Value is string value)
{
- obj.Add(ParseInt(stringValue0, () => section.Path)!);
+ obj.Add(ParseInt(value, () => section.Path));
}
}
}
@@ -49,22 +55,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue2)
+ if (section.Value is string value)
{
- obj[section.Key!] = stringValue2!;
+ obj[section.Key] = value;
}
}
}
- public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
- {
- if (obj is null)
- {
- throw new ArgumentNullException(nameof(obj));
- }
-
- }
-
public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions)
{
if (obj is null)
@@ -74,12 +71,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (!(obj.TryGetValue(section.Key!, out Program.MyClass2? element) && element is not null))
+ if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null))
{
element = new Program.MyClass2();
}
- BindCore(section, ref element!, binderOptions);
- obj[section.Key!] = element;
+ obj[section.Key] = element;
}
}
@@ -90,81 +86,67 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
+
+ obj.MyString = configuration["MyString"]!;
+
+ if (configuration["MyInt"] is string value1)
{
- switch (section.Key)
- {
- case "MyString":
- {
- obj.MyString = configuration["MyString"]!;
- }
- break;
- case "MyInt":
- {
- if (configuration["MyInt"] is string stringValue6)
- {
- obj.MyInt = ParseInt(stringValue6, () => section.Path)!;
- }
- }
- break;
- case "MyList":
- {
- if (HasChildren(section))
- {
- List temp7 = obj.MyList;
- temp7 ??= new List();
- BindCore(section, ref temp7, binderOptions);
- obj.MyList = temp7;
- }
- }
- break;
- case "MyDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp8 = obj.MyDictionary;
- temp8 ??= new Dictionary();
- BindCore(section, ref temp8, binderOptions);
- obj.MyDictionary = temp8;
- }
- }
- break;
- case "MyComplexDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp9 = obj.MyComplexDictionary;
- temp9 ??= new Dictionary();
- BindCore(section, ref temp9, binderOptions);
- obj.MyComplexDictionary = temp9;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path);
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ {
+ List temp4 = obj.MyList;
+ temp4 ??= new List();
+ BindCore(section2, ref temp4, binderOptions);
+ obj.MyList = temp4;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ {
+ Dictionary temp7 = obj.MyDictionary;
+ temp7 ??= new Dictionary();
+ BindCore(section5, ref temp7, binderOptions);
+ obj.MyDictionary = temp7;
}
- if (temp is not null)
+ if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClass)}: {string.Join(", ", temp)}");
+ Dictionary temp10 = obj.MyComplexDictionary;
+ temp10 ??= new Dictionary();
+ BindCore(section8, ref temp10, binderOptions);
+ obj.MyComplexDictionary = temp10;
}
}
- public static bool HasChildren(IConfiguration configuration)
+ /// 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)
{
- foreach (IConfigurationSection section in configuration.GetChildren())
+ if (binderOptions?.ErrorOnUnknownConfiguration is true)
+ {
+ List? temp = null;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (!keys.Value.Contains(section.Key))
+ {
+ (temp ??= new List()).Add($"'{section.Key}'");
+ }
+ }
+ if (temp is not null)
+ {
+ throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}");
+ }
+ }
+ }
+
+ public static IConfiguration? AsConfigWithChildren(IConfiguration configuration)
+ {
+ foreach (IConfigurationSection _ in configuration.GetChildren())
{
- return true;
+ return configuration;
}
- return false;
+ return null;
}
public static BinderOptions? GetBinderOptions(Action? configureOptions)
@@ -182,11 +164,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return binderOptions;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt
index f017f84bd7b2da..ecc9953f810c16 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt
@@ -1,7 +1,9 @@
-//
+//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.
@@ -12,12 +14,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
+ private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
+
public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions)
{
if (obj is null)
@@ -27,9 +33,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue0)
+ if (section.Value is string value)
{
- obj.Add(ParseInt(stringValue0, () => section.Path)!);
+ obj.Add(ParseInt(value, () => section.Path));
}
}
}
@@ -43,22 +49,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue2)
+ if (section.Value is string value)
{
- obj[section.Key!] = stringValue2!;
+ obj[section.Key] = value;
}
}
}
- public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
- {
- if (obj is null)
- {
- throw new ArgumentNullException(nameof(obj));
- }
-
- }
-
public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions)
{
if (obj is null)
@@ -68,12 +65,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (!(obj.TryGetValue(section.Key!, out Program.MyClass2? element) && element is not null))
+ if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null))
{
element = new Program.MyClass2();
}
- BindCore(section, ref element!, binderOptions);
- obj[section.Key!] = element;
+ obj[section.Key] = element;
}
}
@@ -84,88 +80,74 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
+
+ obj.MyString = configuration["MyString"]!;
+
+ if (configuration["MyInt"] is string value1)
{
- switch (section.Key)
- {
- case "MyString":
- {
- obj.MyString = configuration["MyString"]!;
- }
- break;
- case "MyInt":
- {
- if (configuration["MyInt"] is string stringValue6)
- {
- obj.MyInt = ParseInt(stringValue6, () => section.Path)!;
- }
- }
- break;
- case "MyList":
- {
- if (HasChildren(section))
- {
- List temp7 = obj.MyList;
- temp7 ??= new List();
- BindCore(section, ref temp7, binderOptions);
- obj.MyList = temp7;
- }
- }
- break;
- case "MyDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp8 = obj.MyDictionary;
- temp8 ??= new Dictionary();
- BindCore(section, ref temp8, binderOptions);
- obj.MyDictionary = temp8;
- }
- }
- break;
- case "MyComplexDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp9 = obj.MyComplexDictionary;
- temp9 ??= new Dictionary();
- BindCore(section, ref temp9, binderOptions);
- obj.MyComplexDictionary = temp9;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path);
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ {
+ List temp4 = obj.MyList;
+ temp4 ??= new List();
+ BindCore(section2, ref temp4, binderOptions);
+ obj.MyList = temp4;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ {
+ Dictionary temp7 = obj.MyDictionary;
+ temp7 ??= new Dictionary();
+ BindCore(section5, ref temp7, binderOptions);
+ obj.MyDictionary = temp7;
}
- if (temp is not null)
+ if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClass)}: {string.Join(", ", temp)}");
+ Dictionary temp10 = obj.MyComplexDictionary;
+ temp10 ??= new Dictionary();
+ BindCore(section8, ref temp10, binderOptions);
+ obj.MyComplexDictionary = temp10;
}
}
- public static bool HasChildren(IConfiguration configuration)
+ /// 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)
{
- foreach (IConfigurationSection section in configuration.GetChildren())
+ if (binderOptions?.ErrorOnUnknownConfiguration is true)
+ {
+ List? temp = null;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (!keys.Value.Contains(section.Key))
+ {
+ (temp ??= new List()).Add($"'{section.Key}'");
+ }
+ }
+ if (temp is not null)
+ {
+ throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}");
+ }
+ }
+ }
+
+ public static IConfiguration? AsConfigWithChildren(IConfiguration configuration)
+ {
+ foreach (IConfigurationSection _ in configuration.GetChildren())
{
- return true;
+ return configuration;
}
- return false;
+ return null;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt
index f11815e935c67e..6132d8d5e2b0b1 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt
@@ -1,7 +1,9 @@
-//
+//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.
@@ -12,12 +14,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
+ private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
+
public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions)
{
if (obj is null)
@@ -27,9 +33,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue0)
+ if (section.Value is string value)
{
- obj.Add(ParseInt(stringValue0, () => section.Path)!);
+ obj.Add(ParseInt(value, () => section.Path));
}
}
}
@@ -43,22 +49,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue2)
+ if (section.Value is string value)
{
- obj[section.Key!] = stringValue2!;
+ obj[section.Key] = value;
}
}
}
- public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
- {
- if (obj is null)
- {
- throw new ArgumentNullException(nameof(obj));
- }
-
- }
-
public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions)
{
if (obj is null)
@@ -68,12 +65,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (!(obj.TryGetValue(section.Key!, out Program.MyClass2? element) && element is not null))
+ if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null))
{
element = new Program.MyClass2();
}
- BindCore(section, ref element!, binderOptions);
- obj[section.Key!] = element;
+ obj[section.Key] = element;
}
}
@@ -84,81 +80,67 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
+
+ obj.MyString = configuration["MyString"]!;
+
+ if (configuration["MyInt"] is string value1)
{
- switch (section.Key)
- {
- case "MyString":
- {
- obj.MyString = configuration["MyString"]!;
- }
- break;
- case "MyInt":
- {
- if (configuration["MyInt"] is string stringValue6)
- {
- obj.MyInt = ParseInt(stringValue6, () => section.Path)!;
- }
- }
- break;
- case "MyList":
- {
- if (HasChildren(section))
- {
- List temp7 = obj.MyList;
- temp7 ??= new List();
- BindCore(section, ref temp7, binderOptions);
- obj.MyList = temp7;
- }
- }
- break;
- case "MyDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp8 = obj.MyDictionary;
- temp8 ??= new Dictionary();
- BindCore(section, ref temp8, binderOptions);
- obj.MyDictionary = temp8;
- }
- }
- break;
- case "MyComplexDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp9 = obj.MyComplexDictionary;
- temp9 ??= new Dictionary();
- BindCore(section, ref temp9, binderOptions);
- obj.MyComplexDictionary = temp9;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path);
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ {
+ List temp4 = obj.MyList;
+ temp4 ??= new List();
+ BindCore(section2, ref temp4, binderOptions);
+ obj.MyList = temp4;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ {
+ Dictionary temp7 = obj.MyDictionary;
+ temp7 ??= new Dictionary();
+ BindCore(section5, ref temp7, binderOptions);
+ obj.MyDictionary = temp7;
}
- if (temp is not null)
+ if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClass)}: {string.Join(", ", temp)}");
+ Dictionary temp10 = obj.MyComplexDictionary;
+ temp10 ??= new Dictionary();
+ BindCore(section8, ref temp10, binderOptions);
+ obj.MyComplexDictionary = temp10;
}
}
- public static bool HasChildren(IConfiguration configuration)
+ /// 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)
{
- foreach (IConfigurationSection section in configuration.GetChildren())
+ if (binderOptions?.ErrorOnUnknownConfiguration is true)
+ {
+ List? temp = null;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (!keys.Value.Contains(section.Key))
+ {
+ (temp ??= new List()).Add($"'{section.Key}'");
+ }
+ }
+ if (temp is not null)
+ {
+ throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}");
+ }
+ }
+ }
+
+ public static IConfiguration? AsConfigWithChildren(IConfiguration configuration)
+ {
+ foreach (IConfigurationSection _ in configuration.GetChildren())
{
- return true;
+ return configuration;
}
- return false;
+ return null;
}
public static BinderOptions? GetBinderOptions(Action? configureOptions)
@@ -176,11 +158,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return binderOptions;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt
index d96b24a05bb5c6..00570279261ade 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt
@@ -1,7 +1,9 @@
-//
+//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.
@@ -12,12 +14,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
+ private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
+
public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions)
{
if (obj is null)
@@ -27,9 +33,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue0)
+ if (section.Value is string value)
{
- obj.Add(ParseInt(stringValue0, () => section.Path)!);
+ obj.Add(ParseInt(value, () => section.Path));
}
}
}
@@ -43,22 +49,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue2)
+ if (section.Value is string value)
{
- obj[section.Key!] = stringValue2!;
+ obj[section.Key] = value;
}
}
}
- public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
- {
- if (obj is null)
- {
- throw new ArgumentNullException(nameof(obj));
- }
-
- }
-
public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions)
{
if (obj is null)
@@ -68,12 +65,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (!(obj.TryGetValue(section.Key!, out Program.MyClass2? element) && element is not null))
+ if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null))
{
element = new Program.MyClass2();
}
- BindCore(section, ref element!, binderOptions);
- obj[section.Key!] = element;
+ obj[section.Key] = element;
}
}
@@ -84,88 +80,74 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
+
+ obj.MyString = configuration["MyString"]!;
+
+ if (configuration["MyInt"] is string value1)
{
- switch (section.Key)
- {
- case "MyString":
- {
- obj.MyString = configuration["MyString"]!;
- }
- break;
- case "MyInt":
- {
- if (configuration["MyInt"] is string stringValue6)
- {
- obj.MyInt = ParseInt(stringValue6, () => section.Path)!;
- }
- }
- break;
- case "MyList":
- {
- if (HasChildren(section))
- {
- List temp7 = obj.MyList;
- temp7 ??= new List();
- BindCore(section, ref temp7, binderOptions);
- obj.MyList = temp7;
- }
- }
- break;
- case "MyDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp8 = obj.MyDictionary;
- temp8 ??= new Dictionary();
- BindCore(section, ref temp8, binderOptions);
- obj.MyDictionary = temp8;
- }
- }
- break;
- case "MyComplexDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp9 = obj.MyComplexDictionary;
- temp9 ??= new Dictionary();
- BindCore(section, ref temp9, binderOptions);
- obj.MyComplexDictionary = temp9;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path);
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2)
+ {
+ List temp4 = obj.MyList;
+ temp4 ??= new List();
+ BindCore(section2, ref temp4, binderOptions);
+ obj.MyList = temp4;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5)
+ {
+ Dictionary temp7 = obj.MyDictionary;
+ temp7 ??= new Dictionary();
+ BindCore(section5, ref temp7, binderOptions);
+ obj.MyDictionary = temp7;
}
- if (temp is not null)
+ if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClass)}: {string.Join(", ", temp)}");
+ Dictionary temp10 = obj.MyComplexDictionary;
+ temp10 ??= new Dictionary();
+ BindCore(section8, ref temp10, binderOptions);
+ obj.MyComplexDictionary = temp10;
}
}
- public static bool HasChildren(IConfiguration configuration)
+ /// 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)
{
- foreach (IConfigurationSection section in configuration.GetChildren())
+ if (binderOptions?.ErrorOnUnknownConfiguration is true)
+ {
+ List? temp = null;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (!keys.Value.Contains(section.Key))
+ {
+ (temp ??= new List()).Add($"'{section.Key}'");
+ }
+ }
+ if (temp is not null)
+ {
+ throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}");
+ }
+ }
+ }
+
+ public static IConfiguration? AsConfigWithChildren(IConfiguration configuration)
+ {
+ foreach (IConfigurationSection _ in configuration.GetChildren())
{
- return true;
+ return configuration;
}
- return false;
+ return null;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt
index ea9c6ba494ee00..edc299c57eb447 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Attempts to bind the configuration instance to a new instance of type T.
@@ -21,12 +23,17 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
+ private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyArray", "MyDictionary" });
+ private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" });
+
public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions)
{
if (configuration is null)
@@ -67,9 +74,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue2)
+ if (section.Value is string value)
{
- obj.Add(ParseInt(stringValue2, () => section.Path)!);
+ obj.Add(ParseInt(value, () => section.Path));
}
}
}
@@ -81,11 +88,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- var temp3 = new List();
- BindCore(configuration, ref temp3, binderOptions);
+ var temp2 = new List();
+ BindCore(configuration, ref temp2, binderOptions);
int originalCount = obj.Length;
- Array.Resize(ref obj, originalCount + temp3.Count);
- temp3.CopyTo(obj, originalCount);
+ Array.Resize(ref obj, originalCount + temp2.Count);
+ temp2.CopyTo(obj, originalCount);
}
public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions)
@@ -97,9 +104,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue6)
+ if (section.Value is string value)
{
- obj[section.Key!] = stringValue6!;
+ obj[section.Key] = value;
}
}
}
@@ -111,71 +118,37 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
+
+ obj.MyString = configuration["MyString"]!;
+
+ if (configuration["MyInt"] is string value5)
{
- switch (section.Key)
- {
- case "MyString":
- {
- obj.MyString = configuration["MyString"]!;
- }
- break;
- case "MyInt":
- {
- if (configuration["MyInt"] is string stringValue8)
- {
- obj.MyInt = ParseInt(stringValue8, () => section.Path)!;
- }
- }
- break;
- case "MyList":
- {
- if (HasChildren(section))
- {
- List temp9 = obj.MyList;
- temp9 ??= new List();
- BindCore(section, ref temp9, binderOptions);
- obj.MyList = temp9;
- }
- }
- break;
- case "MyArray":
- {
- if (HasChildren(section))
- {
- int[] temp10 = obj.MyArray;
- temp10 ??= new int[0];
- BindCore(section, ref temp10, binderOptions);
- obj.MyArray = temp10;
- }
- }
- break;
- case "MyDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp11 = obj.MyDictionary;
- temp11 ??= new Dictionary();
- BindCore(section, ref temp11, binderOptions);
- obj.MyDictionary = temp11;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ obj.MyInt = ParseInt(value5, () => configuration.GetSection("MyInt").Path);
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section6)
+ {
+ List temp8 = obj.MyList;
+ temp8 ??= new List();
+ BindCore(section6, ref temp8, binderOptions);
+ obj.MyList = temp8;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section9)
+ {
+ int[] temp11 = obj.MyArray;
+ temp11 ??= new int[0];
+ BindCore(section9, ref temp11, binderOptions);
+ obj.MyArray = temp11;
}
- if (temp is not null)
+ if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section12)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClass)}: {string.Join(", ", temp)}");
+ Dictionary temp14 = obj.MyDictionary;
+ temp14 ??= new Dictionary();
+ BindCore(section12, ref temp14, binderOptions);
+ obj.MyDictionary = temp14;
}
}
@@ -186,33 +159,31 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
+
+ if (configuration["MyInt"] is string value15)
{
- switch (section.Key)
- {
- case "MyInt":
- {
- if (configuration["MyInt"] is string stringValue12)
- {
- obj.MyInt = ParseInt(stringValue12, () => section.Path)!;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ obj.MyInt = ParseInt(value15, () => configuration.GetSection("MyInt").Path);
}
+ }
- if (temp is not 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)
+ {
+ if (binderOptions?.ErrorOnUnknownConfiguration is true)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClass2)}: {string.Join(", ", temp)}");
+ List? temp = null;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (!keys.Value.Contains(section.Key))
+ {
+ (temp ??= new List()).Add($"'{section.Key}'");
+ }
+ }
+ if (temp is not null)
+ {
+ throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}");
+ }
}
}
@@ -222,16 +193,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return true;
}
- return HasChildren(configuration);
+ return AsConfigWithChildren(configuration) is not null;
}
- public static bool HasChildren(IConfiguration configuration)
+ public static IConfiguration? AsConfigWithChildren(IConfiguration configuration)
{
- foreach (IConfigurationSection section in configuration.GetChildren())
+ foreach (IConfigurationSection _ in configuration.GetChildren())
{
- return true;
+ return configuration;
}
- return false;
+ return null;
}
public static BinderOptions? GetBinderOptions(Action? configureOptions)
@@ -249,11 +220,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return binderOptions;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt
index 2d5cb75ed42556..c9d49faa937244 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Extracts the value with the specified key and converts it to the specified type.
@@ -21,10 +23,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
@@ -35,46 +39,39 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
+ if (section.Value is not string value)
+ {
+ return null;
+ }
+
if (type == typeof(int))
{
- if (section.Value is string stringValue0)
- {
- return ParseInt(stringValue0, () => section.Path);
- }
+ return ParseInt(value, () => section.Path);
}
if (type == typeof(bool?))
{
- if (section.Value is string stringValue1)
- {
- return ParseBool(stringValue1, () => section.Path);
- }
+ return ParseBool(value, () => section.Path);
}
if (type == typeof(byte[]))
{
- if (section.Value is string stringValue2)
- {
- return ParseByteArray(stringValue2, () => section.Path);
- }
+ return ParseByteArray(value, () => section.Path);
}
if (type == typeof(CultureInfo))
{
- if (section.Value is string stringValue3)
- {
- return ParseCultureInfo(stringValue3, () => section.Path);
- }
+ return ParseCultureInfo(value, () => section.Path);
}
return null;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
@@ -82,11 +79,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- public static bool ParseBool(string stringValue, Func getPath)
+ public static bool ParseBool(string value, Func getPath)
{
try
{
- return bool.Parse(stringValue);
+ return bool.Parse(value);
}
catch (Exception exception)
{
@@ -94,11 +91,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- public static byte[] ParseByteArray(string stringValue, Func getPath)
+ public static byte[] ParseByteArray(string value, Func getPath)
{
try
{
- return Convert.FromBase64String(stringValue);
+ return Convert.FromBase64String(value);
}
catch (Exception exception)
{
@@ -106,11 +103,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
- public static CultureInfo ParseCultureInfo(string stringValue, Func getPath)
+ public static CultureInfo ParseCultureInfo(string value, Func getPath)
{
try
{
- return CultureInfo.GetCultureInfo(stringValue);
+ return CultureInfo.GetCultureInfo(value);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key.generated.txt
index 19ecaa035c9402..17c963bd980a70 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Extracts the value with the specified key and converts it to the specified type.
@@ -12,10 +14,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
@@ -26,22 +30,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
+ if (section.Value is not string value)
+ {
+ return null;
+ }
+
if (type == typeof(int))
{
- if (section.Value is string stringValue0)
- {
- return ParseInt(stringValue0, () => section.Path);
- }
+ return ParseInt(value, () => section.Path);
}
return null;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key_DefaultValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key_DefaultValue.generated.txt
index dac919263ffda0..1148109b9f5a81 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key_DefaultValue.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key_DefaultValue.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Extracts the value with the specified key and converts it to the specified type.
@@ -12,10 +14,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
@@ -26,22 +30,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
+ if (section.Value is not string value)
+ {
+ return null;
+ }
+
if (type == typeof(int))
{
- if (section.Value is string stringValue0)
- {
- return ParseInt(stringValue0, () => section.Path);
- }
+ return ParseInt(value, () => section.Path);
}
return null;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key.generated.txt
index 4e7dbe7bd980c5..c833b20f18dcfe 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Extracts the value with the specified key and converts it to the specified type.
@@ -12,10 +14,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
@@ -26,22 +30,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
+ if (section.Value is not string value)
+ {
+ return null;
+ }
+
if (type == typeof(bool?))
{
- if (section.Value is string stringValue0)
- {
- return ParseBool(stringValue0, () => section.Path);
- }
+ return ParseBool(value, () => section.Path);
}
return null;
}
- public static bool ParseBool(string stringValue, Func getPath)
+ public static bool ParseBool(string value, Func getPath)
{
try
{
- return bool.Parse(stringValue);
+ return bool.Parse(value);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key_DefaultValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key_DefaultValue.generated.txt
index e7eb6cc93f9049..f773f79ce6c2c0 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key_DefaultValue.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key_DefaultValue.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Extracts the value with the specified key and converts it to the specified type.
@@ -11,10 +13,13 @@ internal static class GeneratedConfigurationBinder
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
+ using System;
+ using System.CodeDom.Compiler;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
@@ -25,22 +30,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IConfigurationSection section = configuration.GetSection(key);
+ if (section.Value is not string value)
+ {
+ return null;
+ }
+
if (type == typeof(CultureInfo))
{
- if (section.Value is string stringValue0)
- {
- return ParseCultureInfo(stringValue0, () => section.Path);
- }
+ return ParseCultureInfo(value, () => section.Path);
}
return null;
}
- public static CultureInfo ParseCultureInfo(string stringValue, Func getPath)
+ public static CultureInfo ParseCultureInfo(string value, Func getPath)
{
try
{
- return CultureInfo.GetCultureInfo(stringValue);
+ return CultureInfo.GetCultureInfo(value);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt
index 8bfc62e6b62908..92768a54a644d4 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt
@@ -1,7 +1,9 @@
-//
+//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Attempts to bind the configuration instance to a new instance of type T.
@@ -12,12 +14,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
+ private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyArray", "MyDictionary" });
+
public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions)
{
if (configuration is null)
@@ -51,9 +57,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue1)
+ if (section.Value is string value)
{
- obj.Add(ParseInt(stringValue1, () => section.Path)!);
+ obj.Add(ParseInt(value, () => section.Path));
}
}
}
@@ -65,11 +71,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- var temp2 = new List();
- BindCore(configuration, ref temp2, binderOptions);
+ var temp1 = new List();
+ BindCore(configuration, ref temp1, binderOptions);
int originalCount = obj.Length;
- Array.Resize(ref obj, originalCount + temp2.Count);
- temp2.CopyTo(obj, originalCount);
+ Array.Resize(ref obj, originalCount + temp1.Count);
+ temp1.CopyTo(obj, originalCount);
}
public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions)
@@ -81,9 +87,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue5)
+ if (section.Value is string value)
{
- obj[section.Key!] = stringValue5!;
+ obj[section.Key] = value;
}
}
}
@@ -95,71 +101,57 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
+
+ obj.MyString = configuration["MyString"]!;
+
+ if (configuration["MyInt"] is string value4)
{
- switch (section.Key)
- {
- case "MyString":
- {
- obj.MyString = configuration["MyString"]!;
- }
- break;
- case "MyInt":
- {
- if (configuration["MyInt"] is string stringValue7)
- {
- obj.MyInt = ParseInt(stringValue7, () => section.Path)!;
- }
- }
- break;
- case "MyList":
- {
- if (HasChildren(section))
- {
- List temp8 = obj.MyList;
- temp8 ??= new List();
- BindCore(section, ref temp8, binderOptions);
- obj.MyList = temp8;
- }
- }
- break;
- case "MyArray":
- {
- if (HasChildren(section))
- {
- int[] temp9 = obj.MyArray;
- temp9 ??= new int[0];
- BindCore(section, ref temp9, binderOptions);
- obj.MyArray = temp9;
- }
- }
- break;
- case "MyDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp10 = obj.MyDictionary;
- temp10 ??= new Dictionary();
- BindCore(section, ref temp10, binderOptions);
- obj.MyDictionary = temp10;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path);
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5)
+ {
+ List temp7 = obj.MyList;
+ temp7 ??= new List();
+ BindCore(section5, ref temp7, binderOptions);
+ obj.MyList = temp7;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section8)
+ {
+ int[] temp10 = obj.MyArray;
+ temp10 ??= new int[0];
+ BindCore(section8, ref temp10, binderOptions);
+ obj.MyArray = temp10;
}
- if (temp is not null)
+ if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClass)}: {string.Join(", ", temp)}");
+ Dictionary temp13 = obj.MyDictionary;
+ temp13 ??= new Dictionary();
+ BindCore(section11, ref temp13, binderOptions);
+ obj.MyDictionary = temp13;
+ }
+ }
+
+ /// 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)
+ {
+ if (binderOptions?.ErrorOnUnknownConfiguration is true)
+ {
+ List? temp = null;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (!keys.Value.Contains(section.Key))
+ {
+ (temp ??= new List()).Add($"'{section.Key}'");
+ }
+ }
+ if (temp is not null)
+ {
+ throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}");
+ }
}
}
@@ -169,16 +161,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return true;
}
- return HasChildren(configuration);
+ return AsConfigWithChildren(configuration) is not null;
}
- public static bool HasChildren(IConfiguration configuration)
+ public static IConfiguration? AsConfigWithChildren(IConfiguration configuration)
{
- foreach (IConfigurationSection section in configuration.GetChildren())
+ foreach (IConfigurationSection _ in configuration.GetChildren())
{
- return true;
+ return configuration;
}
- return false;
+ return null;
}
public static BinderOptions? GetBinderOptions(Action? configureOptions)
@@ -196,11 +188,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return binderOptions;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt
index 72d5f029ea51cb..528049d9617e29 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Attempts to bind the configuration instance to a new instance of type T.
@@ -12,12 +14,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
+ private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyArray", "MyDictionary" });
+
public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions)
{
if (configuration is null)
@@ -51,9 +57,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue1)
+ if (section.Value is string value)
{
- obj.Add(ParseInt(stringValue1, () => section.Path)!);
+ obj.Add(ParseInt(value, () => section.Path));
}
}
}
@@ -65,11 +71,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- var temp2 = new List();
- BindCore(configuration, ref temp2, binderOptions);
+ var temp1 = new List();
+ BindCore(configuration, ref temp1, binderOptions);
int originalCount = obj.Length;
- Array.Resize(ref obj, originalCount + temp2.Count);
- temp2.CopyTo(obj, originalCount);
+ Array.Resize(ref obj, originalCount + temp1.Count);
+ temp1.CopyTo(obj, originalCount);
}
public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions)
@@ -81,9 +87,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
foreach (IConfigurationSection section in configuration.GetChildren())
{
- if (section.Value is string stringValue5)
+ if (section.Value is string value)
{
- obj[section.Key!] = stringValue5!;
+ obj[section.Key] = value;
}
}
}
@@ -95,71 +101,57 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
+
+ obj.MyString = configuration["MyString"]!;
+
+ if (configuration["MyInt"] is string value4)
{
- switch (section.Key)
- {
- case "MyString":
- {
- obj.MyString = configuration["MyString"]!;
- }
- break;
- case "MyInt":
- {
- if (configuration["MyInt"] is string stringValue7)
- {
- obj.MyInt = ParseInt(stringValue7, () => section.Path)!;
- }
- }
- break;
- case "MyList":
- {
- if (HasChildren(section))
- {
- List temp8 = obj.MyList;
- temp8 ??= new List();
- BindCore(section, ref temp8, binderOptions);
- obj.MyList = temp8;
- }
- }
- break;
- case "MyArray":
- {
- if (HasChildren(section))
- {
- int[] temp9 = obj.MyArray;
- temp9 ??= new int[0];
- BindCore(section, ref temp9, binderOptions);
- obj.MyArray = temp9;
- }
- }
- break;
- case "MyDictionary":
- {
- if (HasChildren(section))
- {
- Dictionary temp10 = obj.MyDictionary;
- temp10 ??= new Dictionary();
- BindCore(section, ref temp10, binderOptions);
- obj.MyDictionary = temp10;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path);
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5)
+ {
+ List temp7 = obj.MyList;
+ temp7 ??= new List();
+ BindCore(section5, ref temp7, binderOptions);
+ obj.MyList = temp7;
+ }
+
+ if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section8)
+ {
+ int[] temp10 = obj.MyArray;
+ temp10 ??= new int[0];
+ BindCore(section8, ref temp10, binderOptions);
+ obj.MyArray = temp10;
}
- if (temp is not null)
+ if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClass)}: {string.Join(", ", temp)}");
+ Dictionary temp13 = obj.MyDictionary;
+ temp13 ??= new Dictionary();
+ BindCore(section11, ref temp13, binderOptions);
+ obj.MyDictionary = temp13;
+ }
+ }
+
+ /// 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)
+ {
+ if (binderOptions?.ErrorOnUnknownConfiguration is true)
+ {
+ List? temp = null;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (!keys.Value.Contains(section.Key))
+ {
+ (temp ??= new List()).Add($"'{section.Key}'");
+ }
+ }
+ if (temp is not null)
+ {
+ throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}");
+ }
}
}
@@ -169,16 +161,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return true;
}
- return HasChildren(configuration);
+ return AsConfigWithChildren(configuration) is not null;
}
- public static bool HasChildren(IConfiguration configuration)
+ public static IConfiguration? AsConfigWithChildren(IConfiguration configuration)
{
- foreach (IConfigurationSection section in configuration.GetChildren())
+ foreach (IConfigurationSection _ in configuration.GetChildren())
{
- return true;
+ return configuration;
}
- return false;
+ return null;
}
public static BinderOptions? GetBinderOptions(Action? configureOptions)
@@ -196,11 +188,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return binderOptions;
}
- public static int ParseInt(string stringValue, Func getPath)
+ public static int ParseInt(string value, Func getPath)
{
try
{
- return int.Parse(stringValue, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt
index 65cd132591bc6e..01a865f14b23ca 100644
--- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt
+++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt
@@ -1,7 +1,9 @@
//
#nullable enable
+#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// Generated helper providing an AOT and linking compatible implementation for configuration binding.
+[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
{
/// Attempts to bind the configuration instance to a new instance of type T.
@@ -12,12 +14,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
+ using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
/// Provide core binding logic.
- internal static class CoreBindingHelper
+ [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
+ file static class CoreBindingHelper
{
+ private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" });
+
public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions)
{
if (configuration is null)
@@ -49,33 +55,31 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new ArgumentNullException(nameof(obj));
}
- List? temp = null;
- foreach (IConfigurationSection section in configuration.GetChildren())
+ ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
+
+ if (configuration["MyInt"] is string value1)
{
- switch (section.Key)
- {
- case "MyInt":
- {
- if (configuration["MyInt"] is string stringValue1)
- {
- obj.MyInt = ParseInt(stringValue1, () => section.Path)!;
- }
- }
- break;
- default:
- {
- if (binderOptions?.ErrorOnUnknownConfiguration == true)
- {
- (temp ??= new List()).Add($"'{section.Key}'");
- }
- }
- break;
- }
+ obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path);
}
+ }
- if (temp is not 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)
+ {
+ if (binderOptions?.ErrorOnUnknownConfiguration is true)
{
- throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {typeof(Program.MyClass2)}: {string.Join(", ", temp)}");
+ List? temp = null;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (!keys.Value.Contains(section.Key))
+ {
+ (temp ??= new List