using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
EmitGetCoreMethod();
EmitGetValueCoreMethod();
EmitBindCoreMethods();
+ EmitInitializeMethods();
EmitHelperMethods();
_writer.WriteBlockEnd(); // End helper class.
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration, useFullyQualifiedNames: true);
- foreach (TypeSpec type in _generationSpec.RootConfigTypes[BinderMethodSpecifier.Configure])
+ foreach (TypeSpec type in _generationSpec.ConfigTypes[BinderMethodSpecifier.Configure])
{
string typeDisplayString = type.FullyQualifiedDisplayString;
{
EmitBlankLineIfRequired();
_writer.WriteLine($"public static T? {Identifier.Get}<T>(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}) => " +
- $"(T?)({expressionForGetCore}({Identifier.configuration}, typeof(T), {Identifier.configureActions}: null) ?? default(T));");
+ $"(T?)({expressionForGetCore}({Identifier.configuration}, typeof(T), {Identifier.configureOptions}: null) ?? default(T));");
}
if (_generationSpec.ShouldEmitMethods(BinderMethodSpecifier.Get_T_BinderOptions))
{
EmitBlankLineIfRequired();
- _writer.WriteLine($"public static T? {Identifier.Get}<T>(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayName.ActionOfBinderOptions}? {Identifier.configureActions}) => " +
- $"(T?)({expressionForGetCore}({Identifier.configuration}, typeof(T), {Identifier.configureActions}) ?? default(T));");
+ _writer.WriteLine($"public static T? {Identifier.Get}<T>(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayName.ActionOfBinderOptions}? {Identifier.configureOptions}) => " +
+ $"(T?)({expressionForGetCore}({Identifier.configuration}, typeof(T), {Identifier.configureOptions}) ?? default(T));");
}
if (_generationSpec.ShouldEmitMethods(BinderMethodSpecifier.Get_TypeOf))
{
EmitBlankLineIfRequired();
_writer.WriteLine($"public static object? {Identifier.Get}(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayName.Type} {Identifier.type}) => " +
- $"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureActions}: null);");
+ $"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions}: null);");
}
if (_generationSpec.ShouldEmitMethods(BinderMethodSpecifier.Get_TypeOf_BinderOptions))
{
EmitBlankLineIfRequired();
- _writer.WriteLine($"public static object? {Identifier.Get}(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayName.Type} {Identifier.type}, {FullyQualifiedDisplayName.ActionOfBinderOptions}? {Identifier.configureActions}) => " +
- $"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureActions});");
+ _writer.WriteLine($"public static object? {Identifier.Get}(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayName.Type} {Identifier.type}, {FullyQualifiedDisplayName.ActionOfBinderOptions}? {Identifier.configureOptions}) => " +
+ $"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions});");
}
}
return;
}
- Dictionary<BinderMethodSpecifier, HashSet<TypeSpec>> rootConfigTypes = _generationSpec.RootConfigTypes;
+ Dictionary<BinderMethodSpecifier, HashSet<TypeSpec>> rootConfigTypes = _generationSpec.ConfigTypes;
if (rootConfigTypes.TryGetValue(BinderMethodSpecifier.Bind_instance, out HashSet<TypeSpec>? typeSpecs))
{
foreach (TypeSpec type in typeSpecs)
{
- EmitBlankLineIfRequired();
- _writer.WriteLine(
- $"public static void {Identifier.Bind}(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, {type.FullyQualifiedDisplayString} {Identifier.obj}) => " +
- $"{FullyQualifiedDisplayName.Helpers}.{Identifier.BindCore}({Identifier.configuration}, ref {Identifier.obj}, {Identifier.binderOptions}: null);");
+ EmitMethodImplementation(
+ type,
+ additionalParams: GetObjParameter(type),
+ configExpression: Identifier.configuration,
+ configureOptions: false);
}
}
{
foreach (TypeSpec type in typeSpecs)
{
- EmitBlankLineIfRequired();
- _writer.WriteLine(
- $"public static void {Identifier.Bind}(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, {type.FullyQualifiedDisplayString} {Identifier.obj}, {FullyQualifiedDisplayName.ActionOfBinderOptions}? {Identifier.configureActions}) => " +
- $"{FullyQualifiedDisplayName.Helpers}.{Identifier.BindCore}({Identifier.configuration}, ref {Identifier.obj}, {Expression.GetBinderOptions}({Identifier.configureActions}));");
+ EmitMethodImplementation(
+ type,
+ additionalParams: $"{GetObjParameter(type)}, {FullyQualifiedDisplayName.ActionOfBinderOptions}? {Identifier.configureOptions}",
+ configExpression: Identifier.configuration,
+ configureOptions: true);
}
}
{
foreach (TypeSpec type in typeSpecs)
{
- EmitBlankLineIfRequired();
- _writer.WriteLine($"public static void {Identifier.Bind}(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, string {Identifier.key}, {type.FullyQualifiedDisplayString} {Identifier.obj}) => " +
- $"{FullyQualifiedDisplayName.Helpers}.{Identifier.BindCore}({Identifier.configuration}.{Identifier.GetSection}({Identifier.key}), ref {Identifier.obj}, {Identifier.binderOptions}: null);");
+ EmitMethodImplementation(
+ type,
+ additionalParams: $"string {Identifier.key}, {GetObjParameter(type)}",
+ configExpression: $"{Identifier.configuration}.{Identifier.GetSection}({Identifier.key})",
+ configureOptions: false);
}
}
+
+ void EmitMethodImplementation(TypeSpec type, string additionalParams, string configExpression, bool configureOptions)
+ {
+ string binderOptionsArg = configureOptions ? $"{Expression.GetBinderOptions}({Identifier.configureOptions})" : $"{Identifier.binderOptions}: null";
+ string returnExpression = type.CanInitialize
+ ? $"{FullyQualifiedDisplayName.Helpers}.{Identifier.BindCore}({configExpression}, ref {Identifier.obj}, {binderOptionsArg})"
+ : GetInitException(type.InitExceptionMessage);
+
+ EmitBlankLineIfRequired();
+ _writer.WriteLine($"public static void {Identifier.Bind}(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, {additionalParams}) => "
+ + $"{returnExpression};");
+ }
+
+ string GetObjParameter(TypeSpec type) => $"{type.FullyQualifiedDisplayString} {Identifier.obj}";
}
#endregion
return;
}
- _writer.WriteBlockStart($"public static object? {Identifier.GetCore}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, Action<{Identifier.BinderOptions}>? {Identifier.configureActions})");
+ _writer.WriteBlockStart($"public static object? {Identifier.GetCore}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, Action<{Identifier.BinderOptions}>? {Identifier.configureOptions})");
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
- _writer.WriteLine($"{Identifier.BinderOptions}? {Identifier.binderOptions} = {Identifier.GetBinderOptions}({Identifier.configureActions});");
+ _writer.WriteLine($"{Identifier.BinderOptions}? {Identifier.binderOptions} = {Identifier.GetBinderOptions}({Identifier.configureOptions});");
_writer.WriteBlankLine();
EmitIConfigurationHasValueOrChildrenCheck(voidReturn: false);
- if (_generationSpec.RootConfigTypes.TryGetValue(BinderMethodSpecifier.Get, out HashSet<TypeSpec>? types))
+ if (_generationSpec.ConfigTypes.TryGetValue(BinderMethodSpecifier.Get, out HashSet<TypeSpec>? types))
{
foreach (TypeSpec type in types)
{
_writer.WriteBlockStart($"if (type == typeof({type.MinimalDisplayString}))");
- EmitBindLogicFromRootMethod(type, Identifier.obj, InitializationKind.Declaration);
- _writer.WriteLine($"return {Identifier.obj};");
+
+ if (type.InitializationStrategy is InitializationStrategy.None || !EmitInitException(type))
+ {
+ EmitBindLogicFromRootMethod(type, Identifier.obj, InitializationKind.Declaration);
+ _writer.WriteLine($"return {Identifier.obj};");
+ }
+
_writer.WriteBlockEnd();
_writer.WriteBlankLine();
}
_writer.WriteBlankLine();
- foreach (TypeSpec type in _generationSpec.RootConfigTypes[BinderMethodSpecifier.GetValue])
+ foreach (TypeSpec type in _generationSpec.ConfigTypes[BinderMethodSpecifier.GetValue])
{
TypeSpec effectiveType = (type as NullableSpec)?.UnderlyingType ?? type;
_writer.WriteBlockStart($"if (type == typeof({type.MinimalDisplayString}))");
return;
}
- foreach (TypeSpec type in _generationSpec.RootConfigTypes[BinderMethodSpecifier.BindCore])
+ foreach (TypeSpec type in _generationSpec.ConfigTypes[BinderMethodSpecifier.BindCore])
{
if (type.SpecKind is TypeSpecKind.ParsableFromString)
{
private void EmitBindCoreMethod(TypeSpec type)
{
+ Debug.Assert(type.CanInitialize);
+
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();
}
+ private void EmitInitializeMethods()
+ {
+ if (!_generationSpec.ShouldEmitMethods(BinderMethodSpecifier.Initialize))
+ {
+ return;
+ }
+
+ foreach (ObjectSpec type in _generationSpec.ConfigTypes[BinderMethodSpecifier.Initialize])
+ {
+ EmitBlankLineIfRequired();
+ EmitInitializeMethod(type);
+ }
+ }
+
+ private void EmitInitializeMethod(ObjectSpec type)
+ {
+ Debug.Assert(type.CanInitialize);
+
+ List<ParameterSpec> ctorParams = type.ConstructorParameters;
+ IEnumerable<PropertySpec> initOnlyProps = type.Properties.Values.Where(prop => prop.SetOnInit);
+ string displayString = type.MinimalDisplayString;
+
+ _writer.WriteBlockStart($"public static {displayString} {type.InitializeMethodDisplayString}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})");
+
+ foreach (ParameterSpec parameter in ctorParams)
+ {
+ if (!parameter.HasExplicitDefaultValue)
+ {
+ _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<string> argumentList = new();
+
+ foreach (ParameterSpec parameter in ctorParams)
+ {
+ EmitMemberBindLogic(parameter.Name, parameter.Type, parameter.ConfigurationKeyName, configValueMustExist: !parameter.HasExplicitDefaultValue);
+ argumentList.Add(GetExpressionForArgument(parameter));
+ }
+
+ foreach (PropertySpec property in initOnlyProps)
+ {
+ if (property.ShouldBind() && property.MatchingCtorParam is null)
+ {
+ EmitMemberBindLogic(property.Name, property.Type, property.ConfigurationKeyName);
+ }
+ }
+
+ _writer.WriteBlock("""
+ default:
+ {
+ continue;
+ }
+ }
+ }
+ """);
+
+ _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)})";
+ if (!initOnlyProps.Any())
+ {
+ _writer.WriteLine($"{returnExpression};");
+ }
+ else
+ {
+ _writer.WriteBlockStart(returnExpression);
+ 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.WriteBlockEnd(";");
+ }
+
+ // End method.
+ _writer.WriteBlockEnd();
+
+ #region Local helpers
+ void EmitMemberBindLogic(string memberName, TypeSpec memberType, string configurationKeyName, bool configValueMustExist = false)
+ {
+ string lhs = memberName + (configValueMustExist ? $".{Identifier.Value}" : string.Empty);
+
+ _writer.WriteBlockStart($@"case ""{configurationKeyName}"":");
+ EmitMemberBindLogicCore(memberType, lhs);
+
+ if (configValueMustExist)
+ {
+ _writer.WriteLine($"{memberName}.{Identifier.HasConfig} = true;");
+ }
+
+ _writer.WriteBlockEnd();
+ _writer.WriteLine("break;");
+
+ void EmitMemberBindLogicCore(TypeSpec type, string lhs)
+ {
+ TypeSpecKind kind = type.SpecKind;
+
+ if (kind is TypeSpecKind.Nullable)
+ {
+ EmitMemberBindLogicCore(((NullableSpec)type).UnderlyingType, lhs);
+ }
+ else if (kind is TypeSpecKind.ParsableFromString)
+ {
+ EmitBindLogicFromString((ParsableFromStringSpec)type, lhs, Expression.sectionValue, Expression.sectionPath);
+ }
+ else if (!EmitInitException(type))
+ {
+ EmitBindCoreCall(type, lhs, Identifier.section, InitializationKind.SimpleAssignment);
+ }
+ }
+ }
+ #endregion
+ }
+
private void EmitHelperMethods()
{
if (_generationSpec.ShouldEmitMethods(BinderMethodSpecifier.Get | BinderMethodSpecifier.Configure))
private void EmitGetBinderOptionsHelper()
{
_writer.WriteBlock($$"""
- public static {{Identifier.BinderOptions}}? {{Identifier.GetBinderOptions}}(System.Action<BinderOptions>? {{Identifier.configureActions}})
+ public static {{Identifier.BinderOptions}}? {{Identifier.GetBinderOptions}}(System.Action<BinderOptions>? {{Identifier.configureOptions}})
{
- if ({{Identifier.configureActions}} is null)
+ if ({{Identifier.configureOptions}} is null)
{
return null;
}
{{Identifier.BinderOptions}} {{Identifier.binderOptions}} = new();
- {{Identifier.configureActions}}({{Identifier.binderOptions}});
+ {{Identifier.configureOptions}}({{Identifier.binderOptions}});
if ({{Identifier.binderOptions}}.BindNonPublicProperties)
{
}
}
- string exceptionTypeDisplayString = _useFullyQualifiedNames ? FullyQualifiedDisplayName.InvalidOperationException : Identifier.InvalidOperationException;
-
_writer.WriteBlock($$"""
public static {{typeDisplayString}} {{type.ParseMethodName}}(string {{Identifier.stringValue}}, Func<string?> {{Identifier.getPath}})
{
}
catch ({{innerExceptionTypeDisplayString}} {{Identifier.exception}})
{
- throw new {{exceptionTypeDisplayString}}($"{{exceptionArg1}}", {{Identifier.exception}});
+ throw new {{GetInvalidOperationDisplayName()}}($"{{exceptionArg1}}", {{Identifier.exception}});
}
}
""");
switch (type.SpecKind)
{
case TypeSpecKind.Enumerable:
+ case TypeSpecKind.Dictionary:
+ case TypeSpecKind.Object:
{
- EmitBindCoreImplForEnumerable((EnumerableSpec)type);
+ Debug.Assert(type.CanInitialize);
+ EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType);
+ EmitBindCoreImplForComplexType(type);
}
break;
- case TypeSpecKind.Dictionary:
+ case TypeSpecKind.Nullable:
{
- EmitBindCoreImplForDictionary((DictionarySpec)type);
+ EmitBindCoreImpl(((NullableSpec)type).UnderlyingType);
}
break;
case TypeSpecKind.IConfigurationSection:
_writer.WriteLine($"{Identifier.obj} = {Identifier.section};");
}
break;
- case TypeSpecKind.Object:
- {
- EmitBindCoreImplForObject((ObjectSpec)type);
- }
- break;
- case TypeSpecKind.Nullable:
- {
- EmitBindCoreImpl(((NullableSpec)type).UnderlyingType);
- }
- break;
default:
Debug.Fail("Invalid type kind", type.SpecKind.ToString());
break;
}
}
- private void EmitBindCoreImplForEnumerable(EnumerableSpec type)
+ private void EmitBindCoreImplForComplexType(TypeSpec type)
{
- EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType);
-
- if (type.PopulationStrategy is CollectionPopulationStrategy.Array)
+ if (type.InitializationStrategy is InitializationStrategy.Array)
+ {
+ EmitPopulationImplForArray((EnumerableSpec)type);
+ }
+ else if (type is EnumerableSpec enumerable)
+ {
+ EmitPopulationImplForEnumerableWithAdd(enumerable);
+ }
+ else if (type is DictionarySpec dictionary)
{
- EmitPopulationImplForArray(type);
+ EmitBindCoreImplForDictionary(dictionary);
}
else
{
- EmitPopulationImplForEnumerableWithAdd(type);
+ EmitBindCoreImplForObject((ObjectSpec)type);
}
}
private void EmitBindCoreImplForDictionary(DictionarySpec type)
{
- EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType);
+ TypeSpec elementType = type.ElementType;
EmitCollectionCastIfRequired(type, out string objIdentifier);
_writer.WriteBlockStart($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())");
ParsableFromStringSpec keyType = type.KeyType;
- TypeSpec elementType = type.ElementType;
// Parse key
if (keyType.StringParsableTypeKind is StringParsableTypeKind.ConfigValue)
}
else // For complex types:
{
+ Debug.Assert(elementType.CanInitialize);
+
bool isValueType = elementType.IsValueType;
string expressionForElementIsNotNull = $"{Identifier.element} is not null";
string elementTypeDisplayString = elementType.MinimalDisplayString + (elementType.IsValueType ? string.Empty : "?");
if (elementType is CollectionSpec
{
- ConstructionStrategy: ConstructionStrategy.ParameterizedConstructor or ConstructionStrategy.ToEnumerableMethod
+ InitializationStrategy: InitializationStrategy.ParameterizedConstructor or InitializationStrategy.ToEnumerableMethod
} collectionSpec)
{
// This is a read-only collection. If the element exists and is not null,
// we need to copy its contents into a new instance & then append/bind to that.
- string initExpression = collectionSpec.ConstructionStrategy is ConstructionStrategy.ParameterizedConstructor
+ string initExpression = collectionSpec.InitializationStrategy is InitializationStrategy.ParameterizedConstructor
? $"new {collectionSpec.ConcreteType.MinimalDisplayString}({Identifier.element})"
: $"{Identifier.element}.{collectionSpec.ToEnumerableMethodCall!}";
private void EmitBindCoreImplForObject(ObjectSpec type)
{
- Dictionary<string, PropertySpec> properties = type.Properties;
- if (properties.Count == 0)
+ if (type.Properties.Count == 0)
{
return;
}
- EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType);
-
string listOfStringDisplayName = "List<string>";
_writer.WriteLine($"{listOfStringDisplayName}? {Identifier.temp} = null;");
_writer.WriteBlockStart($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())");
_writer.WriteBlockStart($"switch ({Expression.sectionKey})");
- foreach (PropertySpec property in properties.Values)
+ foreach (PropertySpec property in type.Properties.Values)
{
_writer.WriteBlockStart($@"case ""{property.ConfigurationKeyName}"":");
+ bool success = true;
if (property.ShouldBind())
{
- EmitBindCoreImplForProperty(property, property.Type!, parentType: type);
+ success = EmitBindCoreImplForProperty(property, property.Type, parentType: type);
}
_writer.WriteBlockEnd();
- _writer.WriteLine("break;");
+ if (success)
+ {
+ _writer.WriteLine("break;");
+ }
}
_writer.WriteBlock($$"""
}
- private void EmitBindCoreImplForProperty(PropertySpec property, TypeSpec propertyType, TypeSpec parentType)
+ private bool EmitBindCoreImplForProperty(PropertySpec property, TypeSpec propertyType, TypeSpec parentType)
{
string configurationKeyName = property.ConfigurationKeyName;
}
}
break;
- case TypeSpecKind.Array:
- {
- EmitBindCoreCallForProperty(
- property,
- propertyType,
- expressionForPropertyAccess);
- }
- break;
case TypeSpecKind.IConfigurationSection:
{
_writer.WriteLine($"{expressionForPropertyAccess} = {Identifier.section};");
break;
default:
{
+ if (EmitInitException(propertyType))
+ {
+ return false;
+ }
+
EmitBindCoreCallForProperty(property, propertyType, expressionForPropertyAccess);
}
break;
}
+
+ return true;
}
private void EmitBindLogicFromRootMethod(TypeSpec type, string expressionForMemberAccess, InitializationKind initKind)
{
EmitBindLogicFromRootMethod(((NullableSpec)type).UnderlyingType, expressionForMemberAccess, initKind);
}
- else if (kind is TypeSpecKind.ParsableFromString)
+ else
{
- if (initKind is InitializationKind.Declaration)
+ if (kind is TypeSpecKind.ParsableFromString)
{
- EmitCastToIConfigurationSection();
- _writer.WriteLine($"{GetTypeDisplayString(type)} {expressionForMemberAccess} = default!;");
+ if (initKind is InitializationKind.Declaration)
+ {
+ EmitCastToIConfigurationSection();
+ _writer.WriteLine($"{GetTypeDisplayString(type)} {expressionForMemberAccess} = default!;");
+ }
+ else
+ {
+ EmitCastToIConfigurationSection();
+ }
+ EmitBindLogicFromString((ParsableFromStringSpec)type, expressionForMemberAccess, Expression.sectionValue, Expression.sectionPath);
}
else
{
- EmitCastToIConfigurationSection();
+ EmitBindCoreCall(type, expressionForMemberAccess, Identifier.configuration, initKind);
}
- EmitBindLogicFromString((ParsableFromStringSpec)type, expressionForMemberAccess, Expression.sectionValue, Expression.sectionPath);
- }
- else
- {
- EmitBindCoreCall(type, expressionForMemberAccess, Identifier.configuration, initKind);
}
}
string expressionForConfigArg,
InitializationKind initKind)
{
+ Debug.Assert(type.CanInitialize);
+
string tempVarName = GetIncrementalVarName(Identifier.temp);
if (initKind is InitializationKind.AssignmentWithNullCheck)
{
_writer.WriteLine($"{expressionForPropertyAccess} = {tempVarName};");
}
}
- else if (canGet)
+ else
{
- _writer.WriteLine($"{effectivePropertyTypeDisplayString} {tempVarName} = {expressionForPropertyAccess};");
- EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.AssignmentWithNullCheck);
- _writer.WriteLine($@"{Identifier.BindCore}({Identifier.section}, ref {tempVarName}, {Identifier.binderOptions});");
+ if (canGet)
+ {
+ _writer.WriteLine($"{effectivePropertyTypeDisplayString} {tempVarName} = {expressionForPropertyAccess};");
+ EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.AssignmentWithNullCheck);
+ _writer.WriteLine($@"{Identifier.BindCore}({Identifier.section}, ref {tempVarName}, {Identifier.binderOptions});");
- if (canSet)
+ if (canSet)
+ {
+ _writer.WriteLine($"{expressionForPropertyAccess} = {tempVarName};");
+ }
+ }
+ else
{
+ Debug.Assert(canSet);
+ EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.Declaration);
+ _writer.WriteLine($@"{Identifier.BindCore}({Identifier.section}, ref {tempVarName}, {Identifier.binderOptions});");
_writer.WriteLine($"{expressionForPropertyAccess} = {tempVarName};");
}
}
- else
- {
- Debug.Assert(canSet);
- EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.Declaration);
- _writer.WriteLine($@"{Identifier.BindCore}({Identifier.section}, ref {tempVarName}, {Identifier.binderOptions});");
- _writer.WriteLine($"{expressionForPropertyAccess} = {tempVarName};");
- }
_writer.WriteBlockEnd();
}
return;
}
- private void EmitObjectInit(TypeSpec type, string expressionForMemberAccess, InitializationKind initKind)
+ private bool EmitObjectInit(TypeSpec type, string expressionForMemberAccess, InitializationKind initKind)
{
+ Debug.Assert(type.CanInitialize);
+
if (initKind is InitializationKind.None)
{
- return;
+ // Reachable?
+ return true;
}
string expressionForInit;
CollectionSpec? collectionType = type as CollectionSpec;
- string typeDisplayString;
+ string effectiveDisplayString = GetTypeDisplayString(type);
if (collectionType is not null)
{
- if (collectionType is EnumerableSpec { PopulationStrategy: CollectionPopulationStrategy.Array })
+ if (collectionType is EnumerableSpec { InitializationStrategy: InitializationStrategy.Array })
{
- typeDisplayString = GetTypeDisplayString(type);
- expressionForInit = $"new {_arrayBracketsRegex.Replace(typeDisplayString, "[0]", 1)}";
+ expressionForInit = $"new {_arrayBracketsRegex.Replace(effectiveDisplayString, "[0]", 1)}";
}
else
{
- typeDisplayString = GetTypeDisplayString(collectionType.ConcreteType ?? collectionType);
- expressionForInit = $"new {typeDisplayString}()";
+ effectiveDisplayString = GetTypeDisplayString(collectionType.ConcreteType ?? collectionType);
+ expressionForInit = $"new {effectiveDisplayString}()";
}
}
- else if (type.ConstructionStrategy is ConstructionStrategy.ParameterlessConstructor)
+ else if (type.InitializationStrategy is InitializationStrategy.ParameterlessConstructor)
{
- typeDisplayString = GetTypeDisplayString(type);
- expressionForInit = $"new {typeDisplayString}()";
+ expressionForInit = $"new {effectiveDisplayString}()";
}
else
{
- return;
+ 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});";
}
if (initKind == InitializationKind.Declaration)
}
else if (initKind == InitializationKind.AssignmentWithNullCheck)
{
- ConstructionStrategy? collectionConstructionStratey = collectionType?.ConstructionStrategy;
- if (collectionConstructionStratey is ConstructionStrategy.ParameterizedConstructor)
- {
- _writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : new {typeDisplayString}({expressionForMemberAccess});");
- }
- else if (collectionConstructionStratey is ConstructionStrategy.ToEnumerableMethod)
+ if (collectionType is CollectionSpec
+ {
+ InitializationStrategy: InitializationStrategy.ParameterizedConstructor or InitializationStrategy.ToEnumerableMethod
+ })
{
- _writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : {expressionForMemberAccess}.{collectionType.ToEnumerableMethodCall!};");
+ if (collectionType.InitializationStrategy is InitializationStrategy.ParameterizedConstructor)
+ {
+ _writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : new {effectiveDisplayString}({expressionForMemberAccess});");
+ }
+ else
+ {
+ _writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : {expressionForMemberAccess}.{collectionType.ToEnumerableMethodCall!};");
+ }
}
else
{
}
else
{
+ Debug.Assert(initKind is InitializationKind.SimpleAssignment);
_writer.WriteLine($"{expressionForMemberAccess} = {expressionForInit};");
}
+
+ return true;
}
private void EmitCollectionCastIfRequired(CollectionSpec type, out string objIdentifier)
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;
private readonly SourceProductionContext _context;
private readonly KnownTypeSymbols _typeSymbols;
- private readonly Dictionary<BinderMethodSpecifier, HashSet<TypeSpec>> _rootConfigTypes = new();
+ private readonly Dictionary<BinderMethodSpecifier, HashSet<TypeSpec>> _configTypes = new();
private readonly HashSet<ITypeSymbol> _unsupportedTypes = new(SymbolEqualityComparer.Default);
private readonly Dictionary<ITypeSymbol, TypeSpec?> _createdSpecs = new(SymbolEqualityComparer.Default);
private readonly HashSet<string> _typeNamespaces = new()
{
"System",
+ "System.Collections.Generic",
"System.Globalization",
"Microsoft.Extensions.Configuration"
};
}
return new SourceGenerationSpec(
- _rootConfigTypes,
+ _configTypes,
_methodsToGen,
_primitivesForHelperGen,
- _typeNamespaces);
+ _typeNamespaces.ToImmutableSortedSet());
}
private void ProcessBindCall(BinderInvocationOperation binderOperation)
}
}
- private TypeSpec? AddRootConfigType(BinderMethodSpecifier methodGroup, BinderMethodSpecifier overload, ITypeSymbol type, Location? location)
+ private void AddRootConfigType(BinderMethodSpecifier methodGroup, BinderMethodSpecifier overload, ITypeSymbol type, Location? location)
{
if (type is INamedTypeSymbol namedType && ContainsGenericParameters(namedType))
{
- return null;
+ return;
}
- TypeSpec? spec = GetOrCreateTypeSpec(type, location);
- if (spec != null)
+ if (GetOrCreateTypeSpec(type, location) is TypeSpec spec)
{
- AddToRootConfigTypeCache(overload, spec);
- AddToRootConfigTypeCache(methodGroup, spec);
-
- _methodsToGen |= overload;
+ RegisterConfigType(spec, overload);
+ RegisterConfigType(spec, methodGroup, isMethodGroup: true);
}
-
- return spec;
}
private TypeSpec? GetOrCreateTypeSpec(ITypeSymbol type, Location? location = null)
return spec;
}
+ bool canInitialize = true;
+
if (IsNullable(type, out ITypeSymbol? underlyingType))
{
spec = TryGetTypeSpec(underlyingType, ParserDiagnostics.NullableUnderlyingTypeNotSupported, out TypeSpec? underlyingTypeSpec)
_typeNamespaces.Add(@namespace);
}
- _createdSpecs[type] = spec;
- return spec;
+ spec = canInitialize ? spec : null;
+ return _createdSpecs[type] = spec;
void RegisterBindCoreGenType(TypeSpec? spec)
{
if (spec is not null)
{
- AddToRootConfigTypeCache(BinderMethodSpecifier.BindCore, spec);
- _methodsToGen |= BinderMethodSpecifier.BindCore;
+ if (spec.CanInitialize)
+ {
+ canInitialize = true;
+ RegisterConfigType(spec, BinderMethodSpecifier.BindCore);
+ }
}
}
}
- private void AddToRootConfigTypeCache(BinderMethodSpecifier method, TypeSpec spec)
+ private void RegisterConfigType(TypeSpec spec, BinderMethodSpecifier binderMethod, bool isMethodGroup = false)
{
Debug.Assert(spec is not null);
- if (!_rootConfigTypes.TryGetValue(method, out HashSet<TypeSpec> types))
+ if (!_configTypes.TryGetValue(binderMethod, out HashSet<TypeSpec> types))
{
- _rootConfigTypes[method] = types = new HashSet<TypeSpec>();
+ _configTypes[binderMethod] = types = new HashSet<TypeSpec>();
}
types.Add(spec);
+
+ if (!isMethodGroup)
+ {
+ _methodsToGen |= binderMethod;
+ }
}
private static bool IsNullable(ITypeSymbol type, [NotNullWhen(true)] out ITypeSymbol? underlyingType)
return null;
}
- // We want a BindCore method for List<TElement> as a temp holder for the array values.
- EnumerableSpec? listSpec = GetOrCreateTypeSpec(_typeSymbols.List.Construct(arrayType.ElementType)) as EnumerableSpec;
- // We know the element type is supported.
- Debug.Assert(listSpec != null);
- if (listSpec is not null)
- {
- AddToRootConfigTypeCache(BinderMethodSpecifier.BindCore, listSpec);
- }
+ // We want a BindCore method for List<TElement> as a temp holder for the array values. We know the element type is supported.
+ EnumerableSpec listSpec = (GetOrCreateTypeSpec(_typeSymbols.List.Construct(arrayType.ElementType)) as EnumerableSpec)!;
+ RegisterConfigType(listSpec, BinderMethodSpecifier.BindCore);
- return new EnumerableSpec(arrayType)
+ EnumerableSpec spec = new EnumerableSpec(arrayType)
{
Location = location,
ElementType = elementSpec,
ConcreteType = listSpec,
- PopulationStrategy = CollectionPopulationStrategy.Array,
+ InitializationStrategy = InitializationStrategy.Array,
+ PopulationStrategy = CollectionPopulationStrategy.Cast_Then_Add, // Using the concrete list type as a temp holder.
ToEnumerableMethodCall = null,
};
+
+ Debug.Assert(spec.CanInitialize);
+ return spec;
}
private bool IsSupportedArrayType(ITypeSymbol type, Location? location)
private CollectionSpec? CreateCollectionSpec(INamedTypeSymbol type, Location? location)
{
+ CollectionSpec? spec;
if (IsCandidateDictionary(type, out ITypeSymbol keyType, out ITypeSymbol elementType))
{
- return CreateDictionarySpec(type, location, keyType, elementType);
+ spec = CreateDictionarySpec(type, location, keyType, elementType);
+ Debug.Assert(spec is null or DictionarySpec { KeyType: null or ParsableFromStringSpec });
+ }
+ else
+ {
+ spec = CreateEnumerableSpec(type, location);
}
- return CreateEnumerableSpec(type, location);
+ if (spec is not null)
+ {
+ spec.InitExceptionMessage ??= spec.ElementType.InitExceptionMessage;
+ }
+
+ return spec;
}
private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? location, ITypeSymbol keyType, ITypeSymbol elementType)
return null;
}
- ConstructionStrategy constructionStrategy;
+ InitializationStrategy constructionStrategy;
CollectionPopulationStrategy populationStrategy;
INamedTypeSymbol? concreteType = null;
INamedTypeSymbol? populationCastType = null;
string? toEnumerableMethodCall = null;
- if (HasPublicParameterlessCtor(type))
+ if (HasPublicParameterLessCtor(type))
{
- constructionStrategy = ConstructionStrategy.ParameterlessConstructor;
+ constructionStrategy = InitializationStrategy.ParameterlessConstructor;
if (HasAddMethod(type, keyType, elementType))
{
else if (IsInterfaceMatch(type, _typeSymbols.GenericIDictionary_Unbound) || IsInterfaceMatch(type, _typeSymbols.IDictionary))
{
concreteType = _typeSymbols.Dictionary;
- constructionStrategy = ConstructionStrategy.ParameterlessConstructor;
+ constructionStrategy = InitializationStrategy.ParameterlessConstructor;
populationStrategy = CollectionPopulationStrategy.Add;
}
else if (IsInterfaceMatch(type, _typeSymbols.IReadOnlyDictionary_Unbound))
{
concreteType = _typeSymbols.Dictionary;
populationCastType = _typeSymbols.GenericIDictionary;
- constructionStrategy = ConstructionStrategy.ToEnumerableMethod;
+ constructionStrategy = InitializationStrategy.ToEnumerableMethod;
populationStrategy = CollectionPopulationStrategy.Cast_Then_Add;
toEnumerableMethodCall = "ToDictionary(pair => pair.Key, pair => pair.Value)";
_typeNamespaces.Add("System.Linq");
Location = location,
KeyType = (ParsableFromStringSpec)keySpec,
ElementType = elementSpec,
- ConstructionStrategy = constructionStrategy,
+ InitializationStrategy = constructionStrategy,
PopulationStrategy = populationStrategy,
ToEnumerableMethodCall = toEnumerableMethodCall,
};
Debug.Assert(!(populationStrategy is CollectionPopulationStrategy.Cast_Then_Add && populationCastType is null));
- spec.ConcreteType = ConstructGenericCollectionTypeSpec(concreteType, keyType, elementType);
- spec.PopulationCastType = ConstructGenericCollectionTypeSpec(populationCastType, keyType, elementType);
+ spec.ConcreteType = ConstructGenericCollectionSpecIfRequired(concreteType, keyType, elementType);
+ spec.PopulationCastType = ConstructGenericCollectionSpecIfRequired(populationCastType, keyType, elementType);
return spec;
}
return null;
}
- ConstructionStrategy constructionStrategy;
+ InitializationStrategy constructionStrategy;
CollectionPopulationStrategy populationStrategy;
INamedTypeSymbol? concreteType = null;
INamedTypeSymbol? populationCastType = null;
- if (HasPublicParameterlessCtor(type))
+ if (HasPublicParameterLessCtor(type))
{
- constructionStrategy = ConstructionStrategy.ParameterlessConstructor;
+ constructionStrategy = InitializationStrategy.ParameterlessConstructor;
if (HasAddMethod(type, elementType))
{
IsInterfaceMatch(type, _typeSymbols.GenericIList_Unbound))
{
concreteType = _typeSymbols.List;
- constructionStrategy = ConstructionStrategy.ParameterlessConstructor;
+ constructionStrategy = InitializationStrategy.ParameterlessConstructor;
populationStrategy = CollectionPopulationStrategy.Add;
}
else if (IsInterfaceMatch(type, _typeSymbols.GenericIEnumerable_Unbound))
{
concreteType = _typeSymbols.List;
populationCastType = _typeSymbols.GenericICollection;
- constructionStrategy = ConstructionStrategy.ParameterizedConstructor;
+ constructionStrategy = InitializationStrategy.ParameterizedConstructor;
populationStrategy = CollectionPopulationStrategy.Cast_Then_Add;
}
else if (IsInterfaceMatch(type, _typeSymbols.ISet_Unbound))
{
concreteType = _typeSymbols.HashSet;
- constructionStrategy = ConstructionStrategy.ParameterlessConstructor;
+ constructionStrategy = InitializationStrategy.ParameterlessConstructor;
populationStrategy = CollectionPopulationStrategy.Add;
}
else if (IsInterfaceMatch(type, _typeSymbols.IReadOnlySet_Unbound))
{
concreteType = _typeSymbols.HashSet;
populationCastType = _typeSymbols.ISet;
- constructionStrategy = ConstructionStrategy.ParameterizedConstructor;
+ constructionStrategy = InitializationStrategy.ParameterizedConstructor;
populationStrategy = CollectionPopulationStrategy.Cast_Then_Add;
}
else if (IsInterfaceMatch(type, _typeSymbols.IReadOnlyList_Unbound) || IsInterfaceMatch(type, _typeSymbols.IReadOnlyCollection_Unbound))
{
concreteType = _typeSymbols.List;
populationCastType = _typeSymbols.GenericICollection;
- constructionStrategy = ConstructionStrategy.ParameterizedConstructor;
+ constructionStrategy = InitializationStrategy.ParameterizedConstructor;
populationStrategy = CollectionPopulationStrategy.Cast_Then_Add;
}
else
{
Location = location,
ElementType = elementSpec,
- ConstructionStrategy = constructionStrategy,
+ InitializationStrategy = constructionStrategy,
PopulationStrategy = populationStrategy,
ToEnumerableMethodCall = null,
};
Debug.Assert(!(populationStrategy is CollectionPopulationStrategy.Cast_Then_Add && populationCastType is null));
- spec.ConcreteType = ConstructGenericCollectionTypeSpec(concreteType, elementType);
- spec.PopulationCastType = ConstructGenericCollectionTypeSpec(populationCastType, elementType);
+ spec.ConcreteType = ConstructGenericCollectionSpecIfRequired(concreteType, elementType);
+ spec.PopulationCastType = ConstructGenericCollectionSpecIfRequired(populationCastType, elementType);
return spec;
}
private ObjectSpec? CreateObjectSpec(INamedTypeSymbol type, Location? location)
{
- Debug.Assert(!_createdSpecs.ContainsKey(type));
-
// Add spec to cache before traversing properties to avoid stack overflow.
- if (!HasPublicParameterlessCtor(type))
+ ObjectSpec objectSpec = new(type) { Location = location };
+ _createdSpecs.Add(type, objectSpec);
+
+ string typeName = objectSpec.Name;
+ IMethodSymbol? ctor = null;
+ DiagnosticDescriptor? diagnosticDescriptor = null;
+
+ if (!(type.IsAbstract || type.TypeKind is TypeKind.Interface))
{
- ReportUnsupportedType(type, ParserDiagnostics.NeedPublicParameterlessConstructor, location);
- _createdSpecs.Add(type, null);
- return null;
+ IMethodSymbol? parameterlessCtor = null;
+ IMethodSymbol? parameterizedCtor = null;
+ bool hasMultipleParameterizedCtors = false;
+
+ foreach (IMethodSymbol candidate in type.InstanceConstructors)
+ {
+ if (candidate.DeclaredAccessibility is not Accessibility.Public)
+ {
+ continue;
+ }
+
+ if (candidate.Parameters.Length is 0)
+ {
+ parameterlessCtor = candidate;
+ }
+ else if (parameterizedCtor is not null)
+ {
+ hasMultipleParameterizedCtors = true;
+ }
+ else
+ {
+ parameterizedCtor = candidate;
+ }
+ }
+
+ bool hasPublicParameterlessCtor = type.IsValueType || parameterlessCtor is not null;
+ if (!hasPublicParameterlessCtor && hasMultipleParameterizedCtors)
+ {
+ diagnosticDescriptor = ParserDiagnostics.MultipleParameterizedConstructors;
+ objectSpec.InitExceptionMessage = string.Format(ExceptionMessages.MultipleParameterizedConstructors, typeName);
+ }
+
+ ctor = type.IsValueType
+ // Roslyn ctor fetching APIs include paramerterless ctors for structs, unlike System.Reflection.
+ ? parameterizedCtor ?? parameterlessCtor
+ : parameterlessCtor ?? parameterizedCtor;
+ }
+
+ objectSpec.InitializationStrategy = ctor?.Parameters.Length is 0 ? InitializationStrategy.ParameterlessConstructor : InitializationStrategy.ParameterizedConstructor;
+
+ if (ctor is null)
+ {
+ diagnosticDescriptor = ParserDiagnostics.MissingPublicInstanceConstructor;
+ objectSpec.InitExceptionMessage = string.Format(ExceptionMessages.MissingPublicInstanceConstructor, typeName);
+ }
+
+ if (diagnosticDescriptor is not null)
+ {
+ Debug.Assert(objectSpec.InitExceptionMessage is not null);
+ ReportUnsupportedType(type, diagnosticDescriptor);
+ return objectSpec;
}
- ObjectSpec objectSpec = new(type) { Location = location, ConstructionStrategy = ConstructionStrategy.ParameterlessConstructor };
- _createdSpecs.Add(type, objectSpec);
INamedTypeSymbol current = type;
while (current is not null)
{
- foreach (ISymbol member in current.GetMembers())
+ var members = current.GetMembers();
+ foreach (ISymbol member in members)
{
- if (member is IPropertySymbol { IsIndexer: false } property)
+ if (member is IPropertySymbol { IsIndexer: false, IsImplicitlyDeclared: false } property)
{
- if (property.Type is ITypeSymbol { } propertyType)
+ AttributeData? attributeData = property.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, _typeSymbols.ConfigurationKeyNameAttribute));
+ string propertyName = property.Name;
+ string configKeyName = attributeData?.ConstructorArguments.FirstOrDefault().Value as string ?? propertyName;
+
+ TypeSpec? propertyTypeSpec = GetOrCreateTypeSpec(property.Type);
+ if (propertyTypeSpec is null)
+ {
+ _context.ReportDiagnostic(Diagnostic.Create(ParserDiagnostics.PropertyNotSupported, location, new string[] { propertyName, type.ToDisplayString() }));
+ }
+ else
{
- AttributeData? attributeData = property.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, _typeSymbols.ConfigurationKeyNameAttribute));
- string propertyName = property.Name;
- string configKeyName = attributeData?.ConstructorArguments.FirstOrDefault().Value as string ?? propertyName;
+ PropertySpec spec = new(property) { Type = propertyTypeSpec, ConfigurationKeyName = configKeyName };
+ objectSpec.Properties[propertyName] = spec;
+ RegisterHasChildrenHelperForGenIfRequired(propertyTypeSpec);
+ }
+ }
+ }
+ current = current.BaseType;
+ }
- TypeSpec? propertyTypeSpec = GetOrCreateTypeSpec(propertyType);
- PropertySpec spec;
+ if (objectSpec.InitializationStrategy is InitializationStrategy.ParameterizedConstructor)
+ {
+ List<string> missingParameters = new();
+ List<string> invalidParameters = new();
- if (propertyTypeSpec is null)
- {
- _context.ReportDiagnostic(Diagnostic.Create(ParserDiagnostics.PropertyNotSupported, location, new string[] { propertyName, type.ToDisplayString() }));
- }
- else
- {
- RegisterHasChildrenHelperForGenIfRequired(propertyTypeSpec);
- }
+ foreach (IParameterSymbol parameter in ctor.Parameters)
+ {
+ string parameterName = parameter.Name;
+ if (!objectSpec.Properties.TryGetValue(parameterName, out PropertySpec? propertySpec))
+ {
+ missingParameters.Add(parameterName);
+ }
+ else if (parameter.RefKind is not RefKind.None)
+ {
+ invalidParameters.Add(parameterName);
+ }
+ else
+ {
+ ParameterSpec paramSpec = new ParameterSpec(parameter)
+ {
+ Type = propertySpec.Type,
+ ConfigurationKeyName = propertySpec.ConfigurationKeyName,
+ };
- spec = new PropertySpec(property) { Type = propertyTypeSpec, ConfigurationKeyName = configKeyName };
- objectSpec.Properties[configKeyName] = spec;
- }
+ propertySpec.MatchingCtorParam = paramSpec;
+ objectSpec.ConstructorParameters.Add(paramSpec);
}
}
- current = current.BaseType;
+
+ if (invalidParameters.Count > 0)
+ {
+ objectSpec.InitExceptionMessage = string.Format(ExceptionMessages.CannotBindToConstructorParameter, typeName, FormatParams(invalidParameters));
+ }
+ else if (missingParameters.Count > 0)
+ {
+ if (type.IsValueType)
+ {
+ objectSpec.InitializationStrategy = InitializationStrategy.ParameterlessConstructor;
+ }
+ else
+ {
+ objectSpec.InitExceptionMessage = string.Format(ExceptionMessages.ConstructorParametersDoNotMatchProperties, typeName, FormatParams(missingParameters));
+ }
+ }
+
+ if (objectSpec.CanInitialize)
+ {
+ RegisterConfigType(objectSpec, BinderMethodSpecifier.Initialize);
+ }
+
+ static string FormatParams(List<string> names) => string.Join(",", names);
}
+ Debug.Assert((objectSpec.CanInitialize && objectSpec.InitExceptionMessage is null) ||
+ (!objectSpec.CanInitialize && objectSpec.InitExceptionMessage is not null));
+
return objectSpec;
}
private void RegisterHasChildrenHelperForGenIfRequired(TypeSpec type)
{
if (type.SpecKind is TypeSpecKind.Object or
- TypeSpecKind.Array or
TypeSpecKind.Enumerable or
TypeSpecKind.Dictionary)
{
return false;
}
- private static bool HasPublicParameterlessCtor(INamedTypeSymbol type)
- {
- if (type.IsAbstract || type.TypeKind == TypeKind.Interface)
- {
- return false;
- }
-
- if (type is not INamedTypeSymbol namedType)
- {
- return false;
- }
-
- foreach (IMethodSymbol ctor in namedType.InstanceConstructors)
- {
- if (ctor.DeclaredAccessibility == Accessibility.Public && ctor.Parameters.Length == 0)
- {
- return true;
- }
- }
-
- return false;
- }
+ private static bool HasPublicParameterLessCtor(INamedTypeSymbol type) =>
+ type.InstanceConstructors.SingleOrDefault(ctor => ctor.DeclaredAccessibility is Accessibility.Public && ctor.Parameters.Length is 0) is not null;
private static bool HasAddMethod(INamedTypeSymbol type, ITypeSymbol element)
{
private static bool IsEnum(ITypeSymbol type) => type is INamedTypeSymbol { EnumUnderlyingType: INamedTypeSymbol { } };
- private CollectionSpec? ConstructGenericCollectionTypeSpec(INamedTypeSymbol? collectionType, params ITypeSymbol[] parameters) =>
+ private CollectionSpec? ConstructGenericCollectionSpecIfRequired(INamedTypeSymbol? collectionType, params ITypeSymbol[] parameters) =>
(collectionType is not null ? ConstructGenericCollectionSpec(collectionType, parameters) : null);
private CollectionSpec? ConstructGenericCollectionSpec(INamedTypeSymbol type, params ITypeSymbol[] parameters)
// 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;
+using System;
+using System.Diagnostics;
+
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
public sealed partial class ConfigurationBindingGenerator
public static class Identifier
{
public const string binderOptions = nameof(binderOptions);
- public const string configureActions = nameof(configureActions);
+ public const string configureOptions = nameof(configureOptions);
public const string configuration = nameof(configuration);
public const string defaultValue = nameof(defaultValue);
public const string element = nameof(element);
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);
public const string Helpers = nameof(Helpers);
_writer.WriteBlankLine();
}
+ private bool EmitInitException(TypeSpec type)
+ {
+ Debug.Assert(type.InitializationStrategy is not InitializationStrategy.None);
+
+ if (!type.CanInitialize)
+ {
+ _writer.WriteLine(GetInitException(type.InitExceptionMessage) + ";");
+ return true;
+ }
+
+ return false;
+ }
+
+ private string GetInitException(string message) => $@"throw new {GetInvalidOperationDisplayName()}(""{message}"")";
+
private string GetIncrementalVarName(string prefix) => $"{prefix}{_parseValueCount++}";
private string GetTypeDisplayString(TypeSpec type) => _useFullyQualifiedNames ? type.FullyQualifiedDisplayString : type.MinimalDisplayString;
return methodName;
}
+
+ private static string GetExpressionForArgument(ParameterSpec parameter)
+ {
+ string name = parameter.Name + (parameter.HasExplicitDefaultValue ? string.Empty : $".{Identifier.Value}");
+
+ return parameter.RefKind switch
+ {
+ RefKind.None => name,
+ RefKind.Ref => $"ref {name}",
+ RefKind.Out => "out _",
+ RefKind.In => $"in {name}",
+ _ => throw new InvalidOperationException()
+ };
+ }
+
+ private string GetInvalidOperationDisplayName() => _useFullyQualifiedNames ? FullyQualifiedDisplayName.InvalidOperationException : Identifier.InvalidOperationException;
}
}
}
// Runtime exception messages; not localized so we keep them in source.
internal static class ExceptionMessages
{
+ public const string CannotBindToConstructorParameter = "Cannot create instance of type '{0}' because one or more parameters cannot be bound to. Constructor parameters cannot be declared as in, out, or ref. Invalid parameters are: '{1}'";
public const string CannotSpecifyBindNonPublicProperties = "The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.";
+ public const string ConstructorParametersDoNotMatchProperties = "Cannot create instance of type '{0}' because one or more parameters cannot be bound to. Constructor parameters must have corresponding properties. Fields are not supported. Missing properties are: '{1}'";
public const string FailedBinding = "Failed to convert configuration value at '{0}' to type '{1}'.";
public const string MissingConfig = "'{0}' was set on the provided {1}, but the following properties were not found on the instance of {2}: {3}";
+ public const string MissingPublicInstanceConstructor = "Cannot create instance of type '{0}' because it is missing a public instance constructor.";
+ public const string MultipleParameterizedConstructors = "Cannot create instance of type '{0}' because it has multiple public parameterized constructors.";
+ public const string ParameterBeingBoundToIsUnnamed = "Cannot create instance of type '{0}' because one or more parameters are unnamed.";
+ public const string ParameterHasNoMatchingConfig = "Cannot create instance of type '{0}' because parameter '{1}' has no matching config. Each parameter in the constructor that does not have a default value must have a corresponding config entry.";
public const string TypeNotDetectedAsInput = "Unable to bind to type '{0}': generator did not detect the type as input.";
public const string TypeNotSupportedAsInput = "Unable to bind to type '{0}': generator does not support this type as input to this method.";
}
internal static class ParserDiagnostics
{
public static DiagnosticDescriptor TypeNotSupported { get; } = CreateTypeNotSupportedDescriptor(nameof(SR.TypeNotSupported));
- public static DiagnosticDescriptor NeedPublicParameterlessConstructor { get; } = CreateTypeNotSupportedDescriptor(nameof(SR.NeedPublicParameterlessConstructor));
+ public static DiagnosticDescriptor MissingPublicInstanceConstructor { get; } = CreateTypeNotSupportedDescriptor(nameof(SR.MissingPublicInstanceConstructor));
public static DiagnosticDescriptor CollectionNotSupported { get; } = CreateTypeNotSupportedDescriptor(nameof(SR.CollectionNotSupported));
public static DiagnosticDescriptor DictionaryKeyNotSupported { get; } = CreateTypeNotSupportedDescriptor(nameof(SR.DictionaryKeyNotSupported));
public static DiagnosticDescriptor ElementTypeNotSupported { get; } = CreateTypeNotSupportedDescriptor(nameof(SR.ElementTypeNotSupported));
+ public static DiagnosticDescriptor MultipleParameterizedConstructors { get; } = CreateTypeNotSupportedDescriptor(nameof(SR.MultipleParameterizedConstructors));
public static DiagnosticDescriptor MultiDimArraysNotSupported { get; } = CreateTypeNotSupportedDescriptor(nameof(SR.MultiDimArraysNotSupported));
public static DiagnosticDescriptor NullableUnderlyingTypeNotSupported { get; } = CreateTypeNotSupportedDescriptor(nameof(SR.NullableUnderlyingTypeNotSupported));
<Compile Include="$(CommonPath)\Roslyn\GetBestTypeByMetadataName.cs" Link="Common\Roslyn\GetBestTypeByMetadataName.cs" />
<Compile Include="ConfigurationBindingGenerator.cs" />
<Compile Include="ConfigurationBindingGenerator.Emitter.cs" />
- <Compile Include="Helpers\Emitter.Helpers.cs" />
- <Compile Include="Helpers\ParserDiagnostics.cs" />
<Compile Include="ConfigurationBindingGenerator.Parser.cs" />
+ <Compile Include="Helpers\Emitter.Helpers.cs" />
<Compile Include="Helpers\ExceptionMessages.cs" />
<Compile Include="Helpers\KnownTypeSymbols.cs" />
+ <Compile Include="Helpers\ParserDiagnostics.cs" />
+ <Compile Include="Helpers\SourceWriter.cs" />
<Compile Include="Model\BinderInvocationOperation.cs" />
<Compile Include="Model\BinderMethodSpecifier.cs" />
- <Compile Include="Model\SourceGenerationSpec.cs" />
- <Compile Include="Helpers\SourceWriter.cs" />
<Compile Include="Model\CollectionSpec.cs" />
- <Compile Include="Model\ConstructionStrategy.cs" />
<Compile Include="Model\ConfigurationSectionSpec.cs" />
+ <Compile Include="Model\InitializationStrategy.cs" />
<Compile Include="Model\NullableSpec.cs" />
<Compile Include="Model\ObjectSpec.cs" />
+ <Compile Include="Model\ParameterSpec.cs" />
<Compile Include="Model\ParsableFromStringSpec.cs" />
<Compile Include="Model\PropertySpec.cs" />
+ <Compile Include="Model\SourceGenerationSpec.cs" />
<Compile Include="Model\TypeSpec.cs" />
</ItemGroup>
</Project>
// Binding helpers
BindCore = 0x1000,
HasChildren = 0x4000,
+ Initialize = 0x8000,
// Method groups
Bind = Bind_instance | Bind_instance_BinderOptions | Bind_key_instance,
public required CollectionPopulationStrategy PopulationStrategy { get; init; }
+ public override bool CanInitialize => ConcreteType?.CanInitialize ?? CanInitCompexType;
+
+ public override required InitializationStrategy InitializationStrategy { get; set; }
+
public required string? ToEnumerableMethodCall { get; init; }
}
internal enum CollectionPopulationStrategy
{
- Unknown,
- Array,
- Add,
- Cast_Then_Add,
+ Unknown = 0,
+ Add = 1,
+ Cast_Then_Add = 2,
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
- internal enum ConstructionStrategy
+ internal enum InitializationStrategy
{
None = 0,
ParameterlessConstructor = 1,
ParameterizedConstructor = 2,
ToEnumerableMethod = 3,
+ Array = 4,
}
}
// 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
public override TypeSpecKind SpecKind => TypeSpecKind.Nullable;
public required TypeSpec UnderlyingType { get; init; }
+
+ public override string? InitExceptionMessage
+ {
+ get => UnderlyingType.InitExceptionMessage;
+ set => throw new InvalidOperationException();
+ }
}
}
// 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 System.Collections.Generic;
using Microsoft.CodeAnalysis;
{
internal sealed record ObjectSpec : TypeSpec
{
- public ObjectSpec(INamedTypeSymbol type) : base(type) { }
+ public ObjectSpec(INamedTypeSymbol type) : base(type)
+ {
+ InitializeMethodDisplayString = $"Initialize{type.Name.Replace(".", string.Empty).Replace("<", string.Empty).Replace(">", string.Empty)}";
+ }
public override TypeSpecKind SpecKind => TypeSpecKind.Object;
- public Dictionary<string, PropertySpec?> Properties { get; } = new();
+ public override InitializationStrategy InitializationStrategy { get; set; }
+
+ public override bool CanInitialize => CanInitCompexType;
+
+ public Dictionary<string, PropertySpec> Properties { get; } = new(StringComparer.OrdinalIgnoreCase);
+
+ public List<ParameterSpec> ConstructorParameters { get; } = new();
+
+ public string? InitializeMethodDisplayString { get; }
}
}
--- /dev/null
+// 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;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
+{
+ internal sealed record ParameterSpec
+ {
+ public ParameterSpec(IParameterSymbol parameter)
+ {
+ Name = parameter.Name;
+ RefKind = parameter.RefKind;
+
+ HasExplicitDefaultValue = parameter.HasExplicitDefaultValue;
+ if (HasExplicitDefaultValue)
+ {
+ string formatted = SymbolDisplay.FormatPrimitive(parameter.ExplicitDefaultValue, quoteStrings: true, useHexadecimalNumbers: false);
+ DefaultValue = formatted is "null" ? "default!" : formatted;
+ }
+ }
+
+ 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 string DefaultValue { get; } = "default!";
+ }
+}
{
Name = property.Name;
IsStatic = property.IsStatic;
- CanGet = property.GetMethod is IMethodSymbol { DeclaredAccessibility: Accessibility.Public, IsInitOnly: false };
- CanSet = property.SetMethod is IMethodSymbol { DeclaredAccessibility: Accessibility.Public, IsInitOnly: false };
+
+ bool setterIsPublic = property.SetMethod?.DeclaredAccessibility is Accessibility.Public;
+ IsInitOnly = property.SetMethod?.IsInitOnly == true;
+ IsRequired = property.IsRequired;
+ SetOnInit = setterIsPublic && (IsInitOnly || IsRequired);
+ 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 required TypeSpec? Type { get; init; }
-
public required string ConfigurationKeyName { get; init; }
public bool ShouldBind() =>
(CanGet || CanSet) &&
- Type is not null &&
- !(!CanSet && (Type as CollectionSpec)?.ConstructionStrategy is ConstructionStrategy.ParameterizedConstructor);
+ !(!CanSet && (Type as CollectionSpec)?.InitializationStrategy is InitializationStrategy.ParameterizedConstructor);
}
}
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.Collections.Immutable;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal sealed record SourceGenerationSpec(
- Dictionary<BinderMethodSpecifier, HashSet<TypeSpec>> RootConfigTypes,
+ Dictionary<BinderMethodSpecifier, HashSet<TypeSpec>> ConfigTypes,
BinderMethodSpecifier MethodsToGen,
HashSet<ParsableFromStringSpec> PrimitivesForHelperGen,
- HashSet<string> TypeNamespaces)
+ ImmutableSortedSet<string> TypeNamespaces)
{
public bool HasRootMethods() =>
ShouldEmitMethods(BinderMethodSpecifier.Get | BinderMethodSpecifier.Bind | BinderMethodSpecifier.Configure | BinderMethodSpecifier.GetValue);
public TypeSpec(ITypeSymbol type)
{
+ IsValueType = type.IsValueType;
+ Namespace = type.ContainingNamespace?.ToDisplayString();
FullyQualifiedDisplayString = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
MinimalDisplayString = type.ToDisplayString(s_minimalDisplayFormat);
- Namespace = type.ContainingNamespace?.ToDisplayString();
- IsValueType = type.IsValueType;
+ Name = Namespace + "." + MinimalDisplayString.Replace(".", "+");
}
+ public string Name { get; }
+
public string FullyQualifiedDisplayString { get; }
public string MinimalDisplayString { get; }
public abstract TypeSpecKind SpecKind { get; }
- public virtual ConstructionStrategy ConstructionStrategy { get; init; }
+ public virtual InitializationStrategy InitializationStrategy { get; set; }
+
+ public virtual string? InitExceptionMessage { get; set; }
+
+ public virtual bool CanInitialize => true;
/// <summary>
- /// Where in the input compilation we picked up a call to Bind, Get, or Configure.
+ /// Location in the input compilation we picked up a call to Bind, Get, or Configure.
/// </summary>
public required Location? Location { get; init; }
+
+ protected bool CanInitCompexType => InitializationStrategy is not InitializationStrategy.None && InitExceptionMessage is null;
}
internal enum TypeSpecKind
Unknown = 0,
ParsableFromString = 1,
Object = 2,
- Array = 3,
- Enumerable = 4,
- Dictionary = 5,
- IConfigurationSection = 6,
- Nullable = 7,
+ Enumerable = 3,
+ Dictionary = 4,
+ IConfigurationSection = 5,
+ Nullable = 6,
}
}
<data name="LanguageVersionIsNotSupportedTitle" xml:space="preserve">
<value>Language version is required to be at least C# 11</value>
</data>
+ <data name="MissingPublicInstanceConstructor" xml:space="preserve">
+ <value>Cannot create instance of type '{0}' because it is missing a public instance constructor.</value>
+ </data>
<data name="MultiDimArraysNotSupported" xml:space="preserve">
<value>Multidimensional arrays are not supported: '{0}'.</value>
</data>
- <data name="NeedPublicParameterlessConstructor" xml:space="preserve">
- <value>Only objects with public parameterless constructors are supported: '{0}'.</value>
+ <data name="MultipleParameterizedConstructors" xml:space="preserve">
+ <value>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</value>
</data>
<data name="NullableUnderlyingTypeNotSupported" xml:space="preserve">
<value>Nullable underlying type is not supported: '{0}'.</value>
<target state="translated">Verze jazyka musí být alespoň C# 11</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">Multidimenzionální pole se nepodporují: {0}“.</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">Podporují se pouze objekty s veřejnými konstruktory bez parametrů: „{0}“.</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">Die Sprachversion muss mindestens C# 11 sein</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">Mehrdimensionale Arrays werden nicht unterstützt: "{0}".</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">Nur Objekte mit öffentlichen parameterlosen Konstruktoren werden unterstützt: "{0}".</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">La versión del lenguaje debe ser al menos C# 11</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">No se admiten las matrices multidimensionales: "{0}".</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">Solo se admiten objetos con constructores públicos sin parámetros: "{0}".</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">La version du langage doit être au moins C# 11</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">Les tableaux multidimensionnels ne sont pas pris en charge : ‘{0}‘.</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">Seuls les objets avec des constructeurs sans paramètre publics sont pris en charge : ‘{0}‘.</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">La versione del linguaggio deve essere almeno C# 11</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">Matrici multidimensionali non supportate: '{0}'.</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">Sono supportati solo oggetti con costruttori senza parametri pubblici: '{0}'.</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">言語バージョンは少なくとも C# 11 である必要があります</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">多次元配列はサポートされていません: '{0}'.</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">パラメーターなしのパブリック コンストラクターを持つオブジェクトのみがサポートされています: '{0}'。</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">언어 버전은 C# 11 이상이어야 합니다.</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">다차원 배열은 지원되지 않습니다: '{0}'.</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">공용 매개 변수 없는 생성자가 있는 개체만 지원됩니다: '{0}'.</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">Wymagana jest wersja językowa co najmniej C# 11</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">Tablice wielowymiarowe nie są obsługiwane: „{0}”.</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">Obsługiwane są tylko obiekty z publicznymi konstruktorami bez parametrów: „{0}”.</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">A versão do idioma deve ser pelo menos C# 11</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">Matrizes multidimensionais não são suportadas: '{0}'.</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">Somente objetos com construtores públicos sem parâmetros são suportados: '{0}'.</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">Версия языка должна быть не ниже C# 11</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">Многомерные массивы не поддерживаются: "{0}".</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">Поддерживаются только объекты с общедоступными конструкторами без параметров: "{0}".</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">Dil sürümünün en az C# 11 olması gerekir</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">Çok boyutlu diziler desteklenmiyor: '{0}'.</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">Yalnızca genel parametresiz oluşturucular içeren nesneler desteklenmiyor: '{0}'.</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">语言版本必须至少为 C# 11</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">不支持多维数组: '{0}'。</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">仅支持具有公共无参数构造函数的对象: '{0}'。</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
<target state="translated">語言版本要求至少為 C#11</target>
<note />
</trans-unit>
+ <trans-unit id="MissingPublicInstanceConstructor">
+ <source>Cannot create instance of type '{0}' because it is missing a public instance constructor.</source>
+ <target state="new">Cannot create instance of type '{0}' because it is missing a public instance constructor.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultiDimArraysNotSupported">
<source>Multidimensional arrays are not supported: '{0}'.</source>
<target state="translated">不支援多維陣列: '{0}'。</target>
<note />
</trans-unit>
- <trans-unit id="NeedPublicParameterlessConstructor">
- <source>Only objects with public parameterless constructors are supported: '{0}'.</source>
- <target state="translated">僅支援具有公用無參數建構函式的物件: '{0}'。</target>
+ <trans-unit id="MultipleParameterizedConstructors">
+ <source>Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</source>
+ <target state="new">Cannot create instance of type '{0}' because it has multiple public parameterized constructors.</target>
<note />
</trans-unit>
<trans-unit id="NullableUnderlyingTypeNotSupported">
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration;
using Xunit;
}
}
+ public class ClassWithPrimaryCtor(string color, int length)
+ {
+ public string Color { get; } = color;
+ public int Length { get; } = length;
+ }
public record RecordTypeOptions(string Color, int Length);
using System.Globalization;
using System.Linq;
using System.Reflection;
+#if BUILDING_SOURCE_GENERATOR_TESTS
using Microsoft.Extensions.Configuration;
+#endif
using Microsoft.Extensions.Configuration.Test;
using Xunit;
{
public partial class ConfigurationBinderTests
{
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for records.
+ [Fact]
public void BindWithNestedTypesWithReadOnlyProperties()
{
IConfiguration configuration = new ConfigurationBuilder()
Assert.Equal("Dummy", result.Nested.MyProp);
}
+ // Add test for type with parameterless ctor + init-only properties.
+
[Fact]
public void EnumBindCaseInsensitiveNotThrows()
{
exception.Message);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Ensure exception messages are in sync
+ [Fact]
public void ExceptionWhenTryingToBindClassWithoutParameterlessConstructor()
{
var input = new Dictionary<string, string>
exception.Message);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
- public void ExceptionWhenTryingToBindClassWherePropertiesDoMatchConstructorParameters()
+ [Fact]
+ public void ExceptionWhenTryingToBindClassWherePropertiesDoNotMatchConstructorParameters()
{
var input = new Dictionary<string, string>
{
exception.Message);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void ExceptionWhenTryingToBindToConstructorWithMissingConfig()
{
var input = new Dictionary<string, string>
exception.Message);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void ExceptionWhenTryingToBindConfigToClassWhereNoMatchingParameterIsFoundInConstructor()
{
var input = new Dictionary<string, string>
exception.Message);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void BindsToClassConstructorParametersWithDefaultValues()
{
var input = new Dictionary<string, string>
Assert.Equal(42, testOptions.ClassWhereParametersHaveDefaultValueProperty.Age);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void FieldsNotSupported_ExceptionBindingToConstructorWithParameterMatchingAField()
{
var input = new Dictionary<string, string>
exception.Message);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void BindsToRecordPrimaryConstructorParametersWithDefaultValues()
{
var input = new Dictionary<string, string>
Assert.Equal("hello world", options.MyString);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindImmutableClass()
{
var dic = new Dictionary<string, string>
Assert.Equal("Green", options.Color);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindMutableClassWitNestedImmutableObject()
{
var dic = new Dictionary<string, string>
// If the immutable type has multiple public parameterized constructors, then throw
// an exception.
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindImmutableClass_ThrowsOnMultipleParameterizedConstructors()
{
var dic = new Dictionary<string, string>
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
- string expectedMessage = SR.Format(SR.Error_MultipleParameterizedConstructors, "Microsoft.Extensions.Configuration.Binder.Tests.ConfigurationBinderTests+ImmutableClassWithMultipleParameterizedConstructors");
+ string expectedMessage = SR.Format(SR.Error_MultipleParameterizedConstructors, typeof(ImmutableClassWithMultipleParameterizedConstructors));
var ex = Assert.Throws<InvalidOperationException>(() => config.Get<ImmutableClassWithMultipleParameterizedConstructors>());
// If the immutable type has a parameterized constructor, then throw
// that constructor has an 'in' parameter
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindImmutableClass_ThrowsOnParameterizedConstructorWithAnInParameter()
{
var dic = new Dictionary<string, string>
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
- string expectedMessage = SR.Format(SR.Error_CannotBindToConstructorParameter, "Microsoft.Extensions.Configuration.Binder.Tests.ConfigurationBinderTests+ImmutableClassWithOneParameterizedConstructorButWithInParameter", "string1");
+ string expectedMessage = SR.Format(SR.Error_CannotBindToConstructorParameter, typeof(ImmutableClassWithOneParameterizedConstructorButWithInParameter), "string1");
var ex = Assert.Throws<InvalidOperationException>(() => config.Get<ImmutableClassWithOneParameterizedConstructorButWithInParameter>());
// If the immutable type has a parameterized constructors, then throw
// that constructor has a 'ref' parameter
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindImmutableClass_ThrowsOnParameterizedConstructorWithARefParameter()
{
var dic = new Dictionary<string, string>
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
- string expectedMessage = SR.Format(SR.Error_CannotBindToConstructorParameter, "Microsoft.Extensions.Configuration.Binder.Tests.ConfigurationBinderTests+ImmutableClassWithOneParameterizedConstructorButWithRefParameter", "int1");
+ string expectedMessage = SR.Format(SR.Error_CannotBindToConstructorParameter, typeof(ImmutableClassWithOneParameterizedConstructorButWithRefParameter), "int1");
var ex = Assert.Throws<InvalidOperationException>(() => config.Get<ImmutableClassWithOneParameterizedConstructorButWithRefParameter>());
// If the immutable type has a parameterized constructors, then throw
// if the constructor has an 'out' parameter
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindImmutableClass_ThrowsOnParameterizedConstructorWithAnOutParameter()
{
var dic = new Dictionary<string, string>
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
- string expectedMessage = SR.Format(SR.Error_CannotBindToConstructorParameter, "Microsoft.Extensions.Configuration.Binder.Tests.ConfigurationBinderTests+ImmutableClassWithOneParameterizedConstructorButWithOutParameter", "int2");
+ string expectedMessage = SR.Format(SR.Error_CannotBindToConstructorParameter, typeof(ImmutableClassWithOneParameterizedConstructorButWithOutParameter), "int2");
var ex = Assert.Throws<InvalidOperationException>(() => config.Get<ImmutableClassWithOneParameterizedConstructorButWithOutParameter>());
Assert.Equal(expectedMessage, ex.Message);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindMutableStruct_UnmatchedConstructorsAreIgnored()
{
var dic = new Dictionary<string, string>
// If the immutable type has a public parameterized constructor,
// then pick it.
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindImmutableClass_PicksParameterizedConstructorIfNoParameterlessConstructorExists()
{
var dic = new Dictionary<string, string>
Assert.Equal(2, options.Int2);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindSemiImmutableClass()
{
var dic = new Dictionary<string, string>
Assert.Equal(1.23m, options.Thickness);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindSemiImmutableClass_WithInitProperties()
{
var dic = new Dictionary<string, string>
Assert.Equal(1.23m, options.Thickness);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindRecordOptions()
{
var dic = new Dictionary<string, string>
Assert.Equal("Green", options.Color);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
+ public void CanBindClassWithPrimaryCtor()
+ {
+ var dic = new Dictionary<string, string>
+ {
+ {"Length", "42"},
+ {"Color", "Green"},
+ };
+ var configurationBuilder = new ConfigurationBuilder();
+ configurationBuilder.AddInMemoryCollection(dic);
+ var config = configurationBuilder.Build();
+
+ var options = config.Get<ClassWithPrimaryCtor>();
+ Assert.Equal(42, options.Length);
+ Assert.Equal("Green", options.Color);
+ }
+
+ [Fact]
public void CanBindRecordStructOptions()
{
var dic = new Dictionary<string, string>
Assert.Equal("Green", options.Color);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindNestedRecordOptions()
{
var dic = new Dictionary<string, string>
Assert.Equal(24, options.Nested2.ValueB);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindOnParametersAndProperties_PropertiesAreSetAfterTheConstructor()
{
var dic = new Dictionary<string, string>
Assert.Equal("the color is Green", options.Color);
}
- [ConditionalFact(typeof(TestHelpers), nameof(TestHelpers.NotSourceGenMode))] // Need support for parameterized ctors.
+ [Fact]
public void CanBindReadonlyRecordStructOptions()
{
var dic = new Dictionary<string, string>
Assert.Equal(TimeOnly.Parse("18:26:38.7327436"), obj.Prop22);
#endif
}
+
+ [Fact]
+ public void ForClasses_ParameterlessConstructorIsPickedOverParameterized()
+ {
+ string data = """
+ {
+ "MyInt": 9,
+ }
+ """;
+
+ var configuration = TestHelpers.GetConfigurationFromJsonString(data);
+ var obj = configuration.Get<ClassWithParameterlessAndParameterizedCtor>();
+ Assert.Equal(1, obj.MyInt);
+ }
+
+ [Fact]
+ public void ForStructs_ParameterlessConstructorIsPickedOverParameterized()
+ {
+ string data = """
+ {
+ "MyInt": 10,
+ }
+ """;
+
+ var configuration = TestHelpers.GetConfigurationFromJsonString(data);
+ var obj = configuration.Get<ClassWithParameterlessAndParameterizedCtor>();
+ Assert.Equal(1, obj.MyInt);
+ }
+
+ public class ClassWithParameterlessAndParameterizedCtor
+ {
+ public ClassWithParameterlessAndParameterizedCtor() => MyInt = 1;
+
+ public ClassWithParameterlessAndParameterizedCtor(int myInt) => MyInt = 10;
+
+ public int MyInt { get; }
+ }
+
+ public struct StructWithParameterlessAndParameterizedCtor
+ {
+ public StructWithParameterlessAndParameterizedCtor() => MyInt = 1;
+
+ public StructWithParameterlessAndParameterizedCtor(int myInt) => MyInt = 10;
+
+ public int MyInt { get; }
+ }
}
}
{
public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.BindCore(configuration, ref obj, binderOptions: null);
- public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureActions) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.BindCore(configuration, ref obj, global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetBinderOptions(configureActions));
+ public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.BindCore(configuration, ref obj, global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetBinderOptions(configureOptions));
public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.BindCore(configuration.GetSection(key), ref obj, binderOptions: null);
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
- using System;
- using System.Globalization;
using Microsoft.Extensions.Configuration;
+ using System;
using System.Collections.Generic;
+ using System.Globalization;
internal static class Helpers
{
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<string, Program.MyClass2> obj, BinderOptions? binderOptions)
return false;
}
- public static BinderOptions? GetBinderOptions(System.Action<BinderOptions>? configureActions)
+ public static BinderOptions? GetBinderOptions(System.Action<BinderOptions>? configureOptions)
{
- if (configureActions is null)
+ if (configureOptions is null)
{
return null;
}
BinderOptions binderOptions = new();
- configureActions(binderOptions);
+ configureOptions(binderOptions);
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
internal static class GeneratedConfigurationBinder
{
- public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, typeof(T), configureActions: null) ?? default(T));
+ public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, typeof(T), configureOptions: null) ?? default(T));
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
- using System;
- using System.Globalization;
using Microsoft.Extensions.Configuration;
+ using System;
using System.Collections.Generic;
+ using System.Globalization;
using System.Linq;
internal static class Helpers
{
- public static object? GetCore(this IConfiguration configuration, Type type, Action<BinderOptions>? configureActions)
+ public static object? GetCore(this IConfiguration configuration, Type type, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
- BinderOptions? binderOptions = GetBinderOptions(configureActions);
+ BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (!HasValueOrChildren(configuration))
{
}
}
break;
- case "ICustomDictionary":
- {
- }
- break;
- case "ICustomCollection":
- {
- }
- break;
case "IReadOnlyList":
{
if (HasChildren(section))
}
}
break;
- case "UnsupportedIReadOnlyDictionaryUnsupported":
- {
- }
- break;
case "IReadOnlyDictionary":
{
if (HasChildren(section))
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
- using System;
- using System.Globalization;
using Microsoft.Extensions.Configuration;
+ using System;
using System.Collections.Generic;
+ using System.Globalization;
internal static class Helpers
{
internal static class GeneratedConfigurationBinder
{
- public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, typeof(T), configureActions: null) ?? default(T));
+ public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, typeof(T), configureOptions: null) ?? default(T));
- public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureActions) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, typeof(T), configureActions) ?? default(T));
+ public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, typeof(T), configureOptions) ?? default(T));
- public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, type, configureActions: null);
+ public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, type, configureOptions: null);
- public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureActions) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, type, configureActions);
+ public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.GetCore(configuration, type, configureOptions);
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
- using System;
- using System.Globalization;
using Microsoft.Extensions.Configuration;
+ using System;
using System.Collections.Generic;
+ using System.Globalization;
internal static class Helpers
{
- public static object? GetCore(this IConfiguration configuration, Type type, Action<BinderOptions>? configureActions)
+ public static object? GetCore(this IConfiguration configuration, Type type, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
- BinderOptions? binderOptions = GetBinderOptions(configureActions);
+ BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (!HasValueOrChildren(configuration))
{
return false;
}
- public static BinderOptions? GetBinderOptions(System.Action<BinderOptions>? configureActions)
+ public static BinderOptions? GetBinderOptions(System.Action<BinderOptions>? configureOptions)
{
- if (configureActions is null)
+ if (configureOptions is null)
{
return null;
}
BinderOptions binderOptions = new();
- configureActions(binderOptions);
+ configureOptions(binderOptions);
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
+ using Microsoft.Extensions.Configuration;
using System;
+ using System.Collections.Generic;
using System.Globalization;
- using Microsoft.Extensions.Configuration;
internal static class Helpers
{
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
+ using Microsoft.Extensions.Configuration;
using System;
+ using System.Collections.Generic;
using System.Globalization;
- using Microsoft.Extensions.Configuration;
internal static class Helpers
{