1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 17:44:48 +09:00

Use Roslyn interceptors feature in binder gen (#90340)

* Use Roslyn interceptors feature in binder gen

* Fix polymorphic issue and address feedback

* Fix source build issue

* Revert changes to options gen

* Fix
This commit is contained in:
Layomi Akinrinade 2023-08-19 09:16:49 -07:00 committed by GitHub
parent a429b0ca4f
commit a5ff66c825
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 1590 additions and 1199 deletions

View file

@ -270,3 +270,5 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
| Suppression ID | Suppressed Diagnostic ID | Description |
| :----------------------- | :----------------------- | :---------- |
| __`SYSLIBSUPPRESS0001`__ | CA1822 | Do not offer to make methods static when the methods need to be instance methods for a custom marshaller shape. |
| __`SYSLIBSUPPRESS0002`__ | IL2026 | ConfigurationBindingGenerator: suppress RequiresUnreferencedCode diagnostic for binding call that has been intercepted by a generated static variant. |
| __`SYSLIBSUPPRESS0003`__ | IL3050 | ConfigurationBindingGenerator: suppress RequiresDynamicCode diagnostic for binding call that has been intercepted by a generated static variant. |

View file

@ -10,6 +10,7 @@
<UsagePattern IdentityGlob="System.Composition*/*7.*" />
<UsagePattern IdentityGlob="Microsoft.CodeAnalysis*/*4.4.*" />
<UsagePattern IdentityGlob="Microsoft.CodeAnalysis*/*4.5.*" />
<UsagePattern IdentityGlob="Microsoft.CodeAnalysis*/*4.7.*" />
<!-- Allowed and pinned to major version due to https://github.com/dotnet/source-build/issues/3228 -->
<UsagePattern IdentityGlob="Microsoft.NETCore.App.Crossgen2.linux-x64/*8.*" />

View file

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
@ -17,7 +18,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
private readonly SourceGenerationSpec _sourceGenSpec;
private bool _emitBlankLineBeforeNextStatement;
private bool _useFullyQualifiedNames;
private int _valueSuffixIndex;
private static readonly Regex s_arrayBracketsRegex = new(Regex.Escape("[]"));
@ -32,7 +32,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public void Emit()
{
if (!ShouldEmitBinders())
if (!ShouldEmitBindingExtensions())
{
return;
}
@ -42,17 +42,26 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
""");
EmitInterceptsLocationAttrDecl();
EmitStartBlock($"namespace {ProjectName}");
EmitUsingStatements();
_writer.WriteLine();
EmitStartBlock($$"""
{{Expression.GeneratedCodeAnnotation}}
file static class {{Identifier.BindingExtensions}}
""");
EmitBindingExtensions_IConfiguration();
EmitBindingExtensions_OptionsBuilder();
EmitBindingExtensions_IServiceCollection();
EmitCoreBindingHelpers();
EmitEndBlock(); // BindingExtensions class
_useFullyQualifiedNames = true;
EmitBinder_Extensions_IConfiguration();
EmitBinder_Extensions_OptionsBuilder();
EmitBinder_Extensions_IServiceCollection();
EmitEndBlock(); // Binding namespace.
_useFullyQualifiedNames = false;
Emit_CoreBindingHelper();
_context.AddSource($"{Identifier.GeneratedConfigurationBinder}.g.cs", _writer.ToSourceText());
_context.AddSource($"{Identifier.BindingExtensions}.g.cs", _writer.ToSourceText());
}
private void EmitBindCoreCall(
@ -74,7 +83,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (initKind is InitializationKind.AssignmentWithNullCheck)
{
Debug.Assert(!type.IsValueType);
_writer.WriteLine($"{type.MinimalDisplayString}? {tempIdentifier} = {memberAccessExpr};");
_writer.WriteLine($"{type.DisplayString}? {tempIdentifier} = {memberAccessExpr};");
EmitBindCoreCall(tempIdentifier, InitializationKind.AssignmentWithNullCheck);
}
else if (initKind is InitializationKind.None && type.IsValueType)
@ -89,9 +98,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
void EmitBindCoreCall(string objExpression, InitializationKind initKind)
{
string methodDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCore));
string bindCoreCall = $@"{methodDisplayString}({configArgExpr}, ref {objExpression}, {Identifier.binderOptions});";
string bindCoreCall = $@"{nameof(MethodsToGen_CoreBindingHelper.BindCore)}({configArgExpr}, ref {objExpression}, {Identifier.binderOptions});";
EmitObjectInit(objExpression, initKind);
_writer.WriteLine(bindCoreCall);
writeOnSuccess?.Invoke(objExpression);
@ -127,12 +134,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
else if (typeKind is StringParsableTypeKind.Enum)
{
parsedValueExpr = $"ParseEnum<{type.MinimalDisplayString}>({stringValueToParse_Expr}, () => {sectionPathExpr})";
parsedValueExpr = $"ParseEnum<{type.DisplayString}>({stringValueToParse_Expr}, () => {sectionPathExpr})";
}
else
{
string helperMethodDisplayString = GetHelperMethodDisplayString(type.ParseMethodName);
parsedValueExpr = $"{helperMethodDisplayString}({stringValueToParse_Expr}, () => {sectionPathExpr})";
parsedValueExpr = $"{type.ParseMethodName}({stringValueToParse_Expr}, () => {sectionPathExpr})";
}
if (!checkForNullSectionValue)
@ -156,7 +162,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
string initExpr;
CollectionSpec? collectionType = type as CollectionSpec;
string effectiveDisplayString = GetTypeDisplayString(type);
string effectiveDisplayString = type.DisplayString;
if (collectionType is not null)
{
if (collectionType is EnumerableSpec { InitializationStrategy: InitializationStrategy.Array })
@ -165,7 +171,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
else
{
effectiveDisplayString = GetTypeDisplayString(collectionType.ConcreteType ?? collectionType);
effectiveDisplayString = (collectionType.ConcreteType ?? collectionType).DisplayString;
initExpr = $"new {effectiveDisplayString}()";
}
}
@ -215,36 +221,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return true;
}
private void EmitCastToIConfigurationSection()
private void EmitInterceptsLocationAttrDecl()
{
string sectionTypeDisplayString;
string exceptionTypeDisplayString;
if (_useFullyQualifiedNames)
{
sectionTypeDisplayString = "global::Microsoft.Extensions.Configuration.IConfigurationSection";
exceptionTypeDisplayString = FullyQualifiedDisplayString.InvalidOperationException;
}
else
{
sectionTypeDisplayString = Identifier.IConfigurationSection;
exceptionTypeDisplayString = nameof(InvalidOperationException);
}
_writer.WriteLine();
_writer.WriteLine($$"""
if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}})
namespace System.Runtime.CompilerServices
{
throw new {{exceptionTypeDisplayString}}();
using System;
using System.CodeDom.Compiler;
{{Expression.GeneratedCodeAnnotation}}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
""");
_writer.WriteLine();
}
private void EmitUsingStatements()
{
foreach (string @namespace in _sourceGenSpec.Namespaces.ToImmutableSortedSet())
{
_writer.WriteLine($"using {@namespace};");
}
}
private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn)
{
string returnPostfix = voidReturn ? string.Empty : " null";
string methodDisplayString = GetHelperMethodDisplayString(Identifier.HasValueOrChildren);
_writer.WriteLine($$"""
if (!{{methodDisplayString}}({{Identifier.configuration}}))
if (!{{Identifier.HasValueOrChildren}}({{Identifier.configuration}}))
{
return{{returnPostfix}};
}

View file

@ -1,12 +1,14 @@
// 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 System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
@ -147,7 +149,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
// List<string> is used in generated code as a temp holder for formatting
// an error for config properties that don't map to object properties.
_sourceGenSpec.TypeNamespaces.Add("System.Collections.Generic");
_sourceGenSpec.Namespaces.Add("System.Collections.Generic");
spec = CreateObjectSpec(namedType);
}
@ -169,12 +171,36 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
string @namespace = spec.Namespace;
if (@namespace is not null and not "<global namespace>")
{
_sourceGenSpec.TypeNamespaces.Add(@namespace);
_sourceGenSpec.Namespaces.Add(@namespace);
}
return _createdSpecs[type] = spec;
}
private void RegisterTypeForBindCoreMainGen(TypeSpec typeSpec)
{
if (typeSpec.NeedsMemberBinding)
{
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCoreMain, typeSpec);
RegisterTypeForBindCoreGen(typeSpec);
_sourceGenSpec.MethodsToGen_CoreBindingHelper |= MethodsToGen_CoreBindingHelper.AsConfigWithChildren;
}
}
private void RegisterTypeForBindCoreGen(TypeSpec typeSpec)
{
if (typeSpec.NeedsMemberBinding)
{
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, typeSpec);
}
}
private void RegisterTypeForGetCoreGen(TypeSpec typeSpec)
{
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetCore, typeSpec);
_sourceGenSpec.MethodsToGen_CoreBindingHelper |= MethodsToGen_CoreBindingHelper.AsConfigWithChildren;
}
private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, TypeSpec type)
{
if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(method, out HashSet<TypeSpec>? types))
@ -186,14 +212,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
_sourceGenSpec.MethodsToGen_CoreBindingHelper |= method;
}
private void RegisterTypeForBindCoreUntypedGen(TypeSpec typeSpec)
{
if (typeSpec.NeedsMemberBinding)
{
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, typeSpec);
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCoreUntyped, typeSpec);
}
}
/// <summary>
/// Registers interceptors for root binding methods, except for ConfigurationBinder.Bind,
/// which is handled by <see cref="RegisterAsInterceptor_ConfigBinder_BindMethod"/>
/// </summary>
private void RegisterAsInterceptor(Enum method, IInvocationOperation operation) =>
_sourceGenSpec.InterceptionInfo.RegisterCacheEntry(method, new InterceptorLocationInfo(operation));
private static bool IsNullable(ITypeSymbol type, [NotNullWhen(true)] out ITypeSymbol? underlyingType)
{
@ -335,7 +359,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
// 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)!;
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, listSpec);
RegisterTypeForBindCoreGen(listSpec);
EnumerableSpec spec = new EnumerableSpec(arrayType)
{
@ -347,7 +371,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
};
Debug.Assert(spec.CanInitialize);
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, spec);
RegisterTypeForBindCoreGen(spec);
return spec;
}
@ -383,7 +407,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (spec is not null)
{
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, spec);
RegisterTypeForBindCoreGen(spec);
spec.InitExceptionMessage ??= spec.ElementType.InitExceptionMessage;
}
@ -442,7 +466,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
constructionStrategy = InitializationStrategy.ToEnumerableMethod;
populationStrategy = CollectionPopulationStrategy.Cast_Then_Add;
toEnumerableMethodCall = "ToDictionary(pair => pair.Key, pair => pair.Value)";
_sourceGenSpec.TypeNamespaces.Add("System.Linq");
_sourceGenSpec.Namespaces.Add("System.Linq");
}
else
{
@ -711,7 +735,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (objectSpec.NeedsMemberBinding)
{
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, objectSpec);
RegisterTypeForBindCoreGen(objectSpec);
}
return objectSpec;
@ -890,4 +914,19 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
}
public static class ParserExtensions
{
public static void RegisterCacheEntry<TKey, TValue, TEntry>(this Dictionary<TKey, TValue> cache, TKey key, TEntry entry)
where TKey : notnull
where TValue : ICollection<TEntry>, new()
{
if (!cache.TryGetValue(key, out TValue? entryCollection))
{
cache[key] = entryCollection = new TValue();
}
entryCollection.Add(entry);
}
}
}

View file

@ -0,0 +1,70 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
public sealed partial class ConfigurationBindingGenerator
{
/// <summary>
/// Supresses false-positive diagnostics emitted by the linker
/// when analyzing binding invocations that we have intercepted.
/// Workaround for https://github.com/dotnet/roslyn/issues/68669.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class Suppressor : DiagnosticSuppressor
{
private const string Justification = "The target method has been intercepted by a generated static variant.";
/// <summary>
/// Suppression descriptor for IL2026: Members attributed with RequiresUnreferencedCode may break when trimming.
/// </summary>
private static readonly SuppressionDescriptor RUCDiagnostic = new(id: "SYSLIBSUPPRESS0002", suppressedDiagnosticId: "IL2026", Justification);
/// <summary>
/// Suppression descriptor for IL3050: Avoid calling members annotated with 'RequiresDynamicCodeAttribute' when publishing as native AOT.
/// </summary>
private static readonly SuppressionDescriptor RDCDiagnostic = new(id: "SYSLIBSUPPRESS0003", suppressedDiagnosticId: "IL3050", Justification);
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(RUCDiagnostic, RDCDiagnostic);
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
foreach (Diagnostic diagnostic in context.ReportedDiagnostics)
{
string diagnosticId = diagnostic.Id;
if (diagnosticId != RDCDiagnostic.SuppressedDiagnosticId && diagnosticId != RUCDiagnostic.SuppressedDiagnosticId)
{
continue;
}
Location location = diagnostic.AdditionalLocations.Count > 0
? diagnostic.AdditionalLocations[0]
: diagnostic.Location;
bool shouldSuppressDiagnostic =
location.SourceTree is SyntaxTree sourceTree &&
sourceTree.GetRoot().FindNode(location.SourceSpan) is SyntaxNode syntaxNode &&
BinderInvocation.IsCandidateSyntaxNode(syntaxNode) &&
context.GetSemanticModel(sourceTree)
.GetOperation((InvocationExpressionSyntax)syntaxNode, context.CancellationToken) is IInvocationOperation operation &&
BinderInvocation.IsBindingOperation(operation);
if (shouldSuppressDiagnostic)
{
SuppressionDescriptor targetSuppression = diagnosticId == RUCDiagnostic.SuppressedDiagnosticId
? RUCDiagnostic
: RDCDiagnostic;
context.ReportSuppression(Suppression.Create(targetSuppression, diagnostic));
}
}
}
}
}
}

View file

@ -5,7 +5,6 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
@ -15,7 +14,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
[Generator]
public sealed partial class ConfigurationBindingGenerator : IIncrementalGenerator
{
internal const string ProjectName = "Microsoft.Extensions.Configuration.Binder.SourceGeneration";
private static readonly string ProjectName = Emitter.s_assemblyName.Name;
public void Initialize(IncrementalGeneratorInitializationContext context)
{
@ -42,9 +41,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
context.RegisterSourceOutput(inputData, (spc, source) => Execute(source.Item1, source.Item2, spc));
}
/// <summary>
/// Generates source code to optimize binding with ConfigurationBinder.
/// </summary>
private static void Execute(CompilationData compilationData, ImmutableArray<BinderInvocation> inputCalls, SourceProductionContext context)
{
if (inputCalls.IsDefaultOrEmpty)
@ -73,7 +69,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public CompilationData(CSharpCompilation compilation)
{
LanguageVersionIsSupported = compilation.LanguageVersion >= LanguageVersion.CSharp11;
// We don't have a CSharp21 value available yet. Polyfill the value here for forward compat, rather than use the LangugeVersion.Preview enum value.
// https://github.com/dotnet/roslyn/blob/168689931cb4e3150641ec2fb188a64ce4b3b790/src/Compilers/CSharp/Portable/LanguageVersion.cs#L218-L232
const int LangVersion_CSharp12 = 1200;
LanguageVersionIsSupported = (int)compilation.LanguageVersion >= LangVersion_CSharp12;
if (LanguageVersionIsSupported)
{
TypeSymbols = new KnownTypeSymbols(compilation);

View file

@ -2,9 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis;
using SourceGenerators;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
@ -14,91 +12,84 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
private bool ShouldEmitMethods(MethodsToGen_ConfigurationBinder methods) => (_sourceGenSpec.MethodsToGen_ConfigurationBinder & methods) != 0;
private void EmitBinder_Extensions_IConfiguration()
private void EmitBindingExtensions_IConfiguration()
{
Debug.Assert(_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Count <= 3 &&
!_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Keys.Any(overload => (overload & MethodsToGen_ConfigurationBinder.Bind) is 0));
if (!ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Any))
{
return;
}
_emitBlankLineBeforeNextStatement = false;
EmitRootBindingClassStartBlock(Identifier.GeneratedConfigurationBinder);
EmitBindingExtStartRegion(Identifier.IConfiguration);
EmitGetMethods();
EmitGetValueMethods();
EmitBindMethods_ConfigurationBinder();
EmitEndBlock();
_emitBlankLineBeforeNextStatement = true;
EmitBindingExtEndRegion();
}
private void EmitGetMethods()
{
const string expressionForGetCore = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{nameof(MethodsToGen_CoreBindingHelper.GetCore)}";
const string expressionForGetCore = nameof(MethodsToGen_CoreBindingHelper.GetCore);
const string documentation = "Attempts to bind the configuration instance to a new instance of type T.";
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_T))
{
StartMethodDefinition(documentation);
_writer.WriteLine($"public static T? {Identifier.Get}<T>(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}) => " +
StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_T, documentation);
_writer.WriteLine($"public static T? {Identifier.Get}<T>(this {Identifier.IConfiguration} {Identifier.configuration}) => " +
$"(T?)({expressionForGetCore}({Identifier.configuration}, typeof(T), {Identifier.configureOptions}: null) ?? default(T));");
}
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_T_BinderOptions))
{
StartMethodDefinition(documentation);
_writer.WriteLine($"public static T? {Identifier.Get}<T>(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}) => " +
StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_T_BinderOptions, documentation);
_writer.WriteLine($"public static T? {Identifier.Get}<T>(this {Identifier.IConfiguration} {Identifier.configuration}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}) => " +
$"(T?)({expressionForGetCore}({Identifier.configuration}, typeof(T), {Identifier.configureOptions}) ?? default(T));");
}
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_TypeOf))
{
StartMethodDefinition(documentation);
_writer.WriteLine($"public static object? {Identifier.Get}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.Type} {Identifier.type}) => " +
StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_TypeOf, documentation);
_writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}) => " +
$"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions}: null);");
}
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_TypeOf_BinderOptions))
{
StartMethodDefinition(documentation);
_writer.WriteLine($"public static object? {Identifier.Get}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.Type} {Identifier.type}, {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}) => " +
StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_TypeOf_BinderOptions, documentation);
_writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}) => " +
$"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions});");
}
}
private void EmitGetValueMethods()
{
const string expressionForGetValueCore = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{nameof(MethodsToGen_CoreBindingHelper.GetValueCore)}";
const string expressionForGetValueCore = $"{Identifier.BindingExtensions}.{nameof(MethodsToGen_CoreBindingHelper.GetValueCore)}";
const string documentation = "Extracts the value with the specified key and converts it to the specified type.";
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_T_key))
{
StartMethodDefinition(documentation);
_writer.WriteLine($"public static T? {Identifier.GetValue}<T>(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, string {Identifier.key}) => " +
StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_T_key, documentation);
_writer.WriteLine($"public static T? {Identifier.GetValue}<T>(this {Identifier.IConfiguration} {Identifier.configuration}, string {Identifier.key}) => " +
$"(T?)({expressionForGetValueCore}({Identifier.configuration}, typeof(T), {Identifier.key}) ?? default(T));");
}
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_T_key_defaultValue))
{
StartMethodDefinition(documentation);
_writer.WriteLine($"public static T? {Identifier.GetValue}<T>(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, string {Identifier.key}, T {Identifier.defaultValue}) => " +
StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_T_key_defaultValue, documentation);
_writer.WriteLine($"public static T? {Identifier.GetValue}<T>(this {Identifier.IConfiguration} {Identifier.configuration}, string {Identifier.key}, T {Identifier.defaultValue}) => " +
$"(T?)({expressionForGetValueCore}({Identifier.configuration}, typeof(T), {Identifier.key}) ?? {Identifier.defaultValue});");
}
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key))
{
StartMethodDefinition(documentation);
_writer.WriteLine($"public static object? {Identifier.GetValue}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.Type} {Identifier.type}, string {Identifier.key}) => " +
StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key, documentation);
_writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key}) => " +
$"{expressionForGetValueCore}({Identifier.configuration}, {Identifier.type}, {Identifier.key});");
}
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key_defaultValue))
{
StartMethodDefinition(documentation);
_writer.WriteLine($"public static object? {Identifier.GetValue}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.Type} {Identifier.type}, string {Identifier.key}, object? {Identifier.defaultValue}) => " +
StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key_defaultValue, documentation);
_writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key}, object? {Identifier.defaultValue}) => " +
$"{expressionForGetValueCore}({Identifier.configuration}, {Identifier.type}, {Identifier.key}) ?? {Identifier.defaultValue};");
}
}
@ -110,72 +101,71 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
Dictionary<MethodsToGen_ConfigurationBinder, HashSet<TypeSpec>> types = _sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods;
string objParamExpr = $"object? {Identifier.obj}";
if (types.TryGetValue(MethodsToGen_ConfigurationBinder.Bind_instance, out HashSet<TypeSpec>? typeSpecs))
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance))
{
foreach (TypeSpec type in typeSpecs)
{
EmitMethodImplementation(
type,
additionalParams: GetObjParameter(type),
EmitMethods(
MethodsToGen_ConfigurationBinder.Bind_instance,
additionalParams: objParamExpr,
configExpression: Identifier.configuration,
configureOptions: false);
}
}
if (types.TryGetValue(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions, out typeSpecs))
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions))
{
foreach (TypeSpec type in typeSpecs)
{
EmitMethodImplementation(
type,
additionalParams: $"{GetObjParameter(type)}, {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}",
EmitMethods(
MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions,
additionalParams: $"{objParamExpr}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}",
configExpression: Identifier.configuration,
configureOptions: true);
}
}
if (types.TryGetValue(MethodsToGen_ConfigurationBinder.Bind_key_instance, out typeSpecs))
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_key_instance))
{
foreach (TypeSpec type in typeSpecs)
{
EmitMethodImplementation(
type,
additionalParams: $"string {Identifier.key}, {GetObjParameter(type)}",
EmitMethods(
MethodsToGen_ConfigurationBinder.Bind_key_instance,
additionalParams: $"string {Identifier.key}, {objParamExpr}",
configExpression: $"{Expression.configurationGetSection}({Identifier.key})",
configureOptions: false);
}
}
void EmitMethodImplementation(TypeSpec type, string additionalParams, string configExpression, bool configureOptions)
void EmitMethods(MethodsToGen_ConfigurationBinder method, string additionalParams, string configExpression, bool configureOptions)
{
string binderOptionsArg = configureOptions ? $"{Expression.GetBinderOptions}({Identifier.configureOptions})" : $"{Identifier.binderOptions}: null";
string returnExpression;
if (type.CanInitialize)
foreach (KeyValuePair<TypeSpec, List<InterceptorLocationInfo>> pair in _sourceGenSpec.InterceptionInfo_ConfigBinder.GetOverloadInfo(method))
{
returnExpression = type.NeedsMemberBinding
? $"{FullyQualifiedDisplayString.CoreBindingHelper}.{nameof(MethodsToGen_CoreBindingHelper.BindCore)}({configExpression}, ref {Identifier.obj}, {binderOptionsArg})"
: "{ }";
}
else
(TypeSpec type, List<InterceptorLocationInfo> interceptorInfoList) = (pair.Key, pair.Value);
EmitBlankLineIfRequired();
_writer.WriteLine($"/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>");
EmitInterceptsLocationAnnotations(interceptorInfoList);
EmitStartBlock($"public static void {Identifier.Bind}_{type.DisplayString.ToIdentifierSubstring()}(this {Identifier.IConfiguration} {Identifier.configuration}, {additionalParams})");
if (!EmitInitException(type) && type.NeedsMemberBinding)
{
returnExpression = GetInitException(type.InitExceptionMessage);
string binderOptionsArg = configureOptions ? $"{Identifier.GetBinderOptions}({Identifier.configureOptions})" : $"{Identifier.binderOptions}: null";
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
if (!type.IsValueType)
{
EmitCheckForNullArgument_WithBlankLine(Identifier.obj);
}
_writer.WriteLine($$"""
var {{Identifier.typedObj}} = ({{type.EffectiveType.DisplayString}}){{Identifier.obj}};
{{nameof(MethodsToGen_CoreBindingHelper.BindCore)}}({{configExpression}}, ref {{Identifier.typedObj}}, {{binderOptionsArg}});
""");
}
StartMethodDefinition("Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.");
_writer.WriteLine($"public static void {Identifier.Bind}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {additionalParams}) => "
+ $"{returnExpression};");
EmitEndBlock();
}
}
}
string GetObjParameter(TypeSpec type) => $"{type.FullyQualifiedDisplayString} {Identifier.obj}";
}
private void StartMethodDefinition(string documentation)
private void StartMethodDefinition(MethodsToGen_ConfigurationBinder method, string documentation)
{
EmitBlankLineIfRequired();
_writer.WriteLine($"/// <summary>{documentation}</summary>");
EmitInterceptsLocationAnnotations(method);
}
}
}

View file

@ -3,10 +3,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis;
using SourceGenerators;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
@ -16,41 +16,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
private bool ShouldEmitMethods(MethodsToGen_CoreBindingHelper methods) => (_sourceGenSpec.MethodsToGen_CoreBindingHelper & methods) != 0;
private void Emit_CoreBindingHelper()
private void EmitCoreBindingHelpers()
{
Debug.Assert(_emitBlankLineBeforeNextStatement);
_writer.WriteLine();
_emitBlankLineBeforeNextStatement = false;
EmitStartBlock($"namespace {ProjectName}");
EmitHelperUsingStatements();
_writer.WriteLine();
EmitStartBlock($$"""
/// <summary>Provide core binding logic.</summary>
{{GetGeneratedCodeAttributeSrc()}}
file static class {{Identifier.CoreBindingHelper}}
""");
EmitBindingExtStartRegion("Core binding");
EmitConfigurationKeyCaches();
EmitGetCoreMethod();
EmitGetValueCoreMethod();
EmitBindCoreUntypedMethod();
EmitBindCoreMainMethod();
EmitBindCoreMethods();
EmitInitializeMethods();
EmitHelperMethods();
EmitEndBlock(); // End helper class.
EmitEndBlock(); // End namespace.
}
private void EmitHelperUsingStatements()
{
foreach (string @namespace in _sourceGenSpec.TypeNamespaces.ToImmutableSortedSet())
{
_writer.WriteLine($"using {@namespace};");
}
EmitBindingExtEndRegion();
}
private void EmitConfigurationKeyCaches()
@ -60,6 +37,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
EmitBlankLineIfRequired();
foreach (TypeSpec type in targetTypes)
{
if (type is not ObjectSpec objectType)
@ -73,10 +52,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
string configKeysSource = string.Join(", ", keys);
string fieldName = GetConfigKeyCacheFieldName(objectType);
_writer.WriteLine($@"private readonly static Lazy<{MinimalDisplayString.HashSetOfString}> {fieldName} = new(() => new {MinimalDisplayString.HashSetOfString}(StringComparer.OrdinalIgnoreCase) {{ {configKeysSource} }});");
_writer.WriteLine($@"private readonly static Lazy<{TypeDisplayString.HashSetOfString}> {fieldName} = new(() => new {TypeDisplayString.HashSetOfString}(StringComparer.OrdinalIgnoreCase) {{ {configKeysSource} }});");
}
_emitBlankLineBeforeNextStatement = true;
}
private void EmitGetCoreMethod()
@ -96,16 +73,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
EmitIConfigurationHasValueOrChildrenCheck(voidReturn: false);
bool isFirstType = true;
foreach (TypeSpec type in types)
{
TypeSpec effectiveType = type.EffectiveType;
TypeSpecKind kind = effectiveType.SpecKind;
string conditionKindExpr = GetConditionKindExpr(ref isFirstType);
EmitStartBlock($"if (type == typeof({type.MinimalDisplayString}))");
EmitStartBlock($"{conditionKindExpr} ({Identifier.type} == typeof({type.DisplayString}))");
if (effectiveType is ParsableFromStringSpec stringParsableType)
{
EmitCastToIConfigurationSection();
_writer.WriteLine($$"""
if ({{Identifier.configuration}} is not {{Identifier.IConfigurationSection}} {{Identifier.section}})
{
throw new {{Identifier.InvalidOperationException}}();
}
""");
EmitBindLogicFromString(
stringParsableType,
Expression.sectionValue,
@ -121,9 +106,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
EmitEndBlock();
_writer.WriteLine();
}
_writer.WriteLine();
Emit_NotSupportedException_TypeNotDetectedAsInput();
EmitEndBlock();
_emitBlankLineBeforeNextStatement = true;
@ -152,9 +137,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
_writer.WriteLine();
bool isFirstType = true;
foreach (TypeSpec type in targetTypes)
{
EmitStartBlock($"if ({Identifier.type} == typeof({type.MinimalDisplayString}))");
string conditionKindExpr = GetConditionKindExpr(ref isFirstType);
EmitStartBlock($"{conditionKindExpr} ({Identifier.type} == typeof({type.DisplayString}))");
EmitBindLogicFromString(
(ParsableFromStringSpec)type.EffectiveType,
@ -165,51 +152,48 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
useIncrementalStringValueIdentifier: false);
EmitEndBlock();
_writer.WriteLine();
}
_writer.WriteLine();
_writer.WriteLine("return null;");
EmitEndBlock();
_emitBlankLineBeforeNextStatement = true;
}
private void EmitBindCoreUntypedMethod()
private void EmitBindCoreMainMethod()
{
if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(MethodsToGen_CoreBindingHelper.BindCoreUntyped, out HashSet<TypeSpec>? targetTypes))
if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(MethodsToGen_CoreBindingHelper.BindCoreMain, out HashSet<TypeSpec>? targetTypes))
{
return;
}
EmitBlankLineIfRequired();
EmitStartBlock($"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}(this {Identifier.IConfiguration} {Identifier.configuration}, object {Identifier.obj}, Type {Identifier.type}, {MinimalDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})");
EmitStartBlock($"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCoreMain)}({Identifier.IConfiguration} {Identifier.configuration}, object {Identifier.obj}, Type {Identifier.type}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})");
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
EmitCheckForNullArgument_WithBlankLine(Identifier.obj);
EmitIConfigurationHasValueOrChildrenCheck(voidReturn: true);
_writer.WriteLine($"{Identifier.BinderOptions}? {Identifier.binderOptions} = {Identifier.GetBinderOptions}({Identifier.configureOptions});");
_writer.WriteLine();
EmitIConfigurationHasValueOrChildrenCheck(voidReturn: true);
bool isFirstType = true;
foreach (TypeSpec type in targetTypes)
{
EmitStartBlock($"if (type == typeof({type.MinimalDisplayString}))");
TypeSpec effectiveType = type.EffectiveType;
string conditionKindExpr = GetConditionKindExpr(ref isFirstType);
EmitStartBlock($"{conditionKindExpr} ({Identifier.type} == typeof({type.DisplayString}))");
if (!EmitInitException(effectiveType))
{
_writer.WriteLine($"var {Identifier.temp} = ({effectiveType.MinimalDisplayString}){Identifier.obj};");
_writer.WriteLine($"var {Identifier.temp} = ({effectiveType.DisplayString}){Identifier.obj};");
EmitBindCoreCall(type, Identifier.temp, Identifier.configuration, InitializationKind.None);
_writer.WriteLine($"return;");
}
EmitEndBlock();
_writer.WriteLine();
}
_writer.WriteLine();
Emit_NotSupportedException_TypeNotDetectedAsInput();
EmitEndBlock();
_emitBlankLineBeforeNextStatement = true;
}
private void EmitBindCoreMethods()
@ -231,11 +215,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
Debug.Assert(type.CanInitialize);
string objParameterExpression = $"ref {type.MinimalDisplayString} {Identifier.obj}";
string objParameterExpression = $"ref {type.DisplayString} {Identifier.obj}";
EmitStartBlock(@$"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCore)}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression}, {Identifier.BinderOptions}? {Identifier.binderOptions})");
EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType);
TypeSpec effectiveType = type.EffectiveType;
if (effectiveType is EnumerableSpec enumerable)
{
@ -281,9 +263,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
List<ParameterSpec> ctorParams = type.ConstructorParameters;
IEnumerable<PropertySpec> initOnlyProps = type.Properties.Values.Where(prop => prop is { SetOnInit: true });
List<string> ctorArgList = new();
string displayString = type.MinimalDisplayString;
string displayString = type.DisplayString;
EmitStartBlock($"public static {type.MinimalDisplayString} {GetInitalizeMethodDisplayString(type)}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})");
EmitStartBlock($"public static {type.DisplayString} {GetInitalizeMethodDisplayString(type)}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})");
_emitBlankLineBeforeNextStatement = false;
foreach (ParameterSpec parameter in ctorParams)
@ -335,7 +317,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
TypeSpec memberType = member.Type;
bool errorOnFailedBinding = member.ErrorOnFailedBinding;
string parsedMemberIdentifierDeclarationPrefix = $"{memberType.MinimalDisplayString} {member.Name}";
string parsedMemberIdentifierDeclarationPrefix = $"{memberType.DisplayString} {member.Name}";
string parsedMemberIdentifier;
if (memberType is ParsableFromStringSpec { StringParsableTypeKind: StringParsableTypeKind.AssignFromSectionValue })
@ -344,7 +326,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (errorOnFailedBinding)
{
string condition = $@" if ({Identifier.configuration}[""{member.ConfigurationKeyName}""] is not {memberType.MinimalDisplayString} {member.Name})";
string condition = $@" if ({Identifier.configuration}[""{member.ConfigurationKeyName}""] is not {memberType.DisplayString} {member.Name})";
EmitThrowBlock(condition);
_writer.WriteLine();
return;
@ -393,40 +375,42 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
_writer.WriteLine($$"""
{{condition}}
{
throw new {{GetInvalidOperationDisplayName()}}("{{string.Format(ExceptionMessages.ParameterHasNoMatchingConfig, type.Name, member.Name)}}");
throw new {{Identifier.InvalidOperationException}}("{{string.Format(ExceptionMessages.ParameterHasNoMatchingConfig, type.Name, member.Name)}}");
}
""");
}
}
private void EmitHelperMethods()
{
// Emitted if we are to bind objects with complex members, or if we're emitting BindCoreMain or GetCore methods.
bool emitAsConfigWithChildren = ShouldEmitMethods(MethodsToGen_CoreBindingHelper.AsConfigWithChildren);
if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore))
{
EmitBlankLineIfRequired();
EmitValidateConfigurationKeysMethod();
}
if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCoreUntyped | MethodsToGen_CoreBindingHelper.GetCore))
if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCoreMain | MethodsToGen_CoreBindingHelper.GetCore))
{
_writer.WriteLine();
// HasValueOrChildren references this method.
Debug.Assert(emitAsConfigWithChildren);
EmitBlankLineIfRequired();
EmitHasValueOrChildrenMethod();
_writer.WriteLine();
EmitAsConfigWithChildrenMethod();
_emitBlankLineBeforeNextStatement = true;
}
else if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.AsConfigWithChildren))
{
_writer.WriteLine();
EmitAsConfigWithChildrenMethod();
_emitBlankLineBeforeNextStatement = true;
}
if (ShouldEmitMethods(
MethodsToGen_CoreBindingHelper.BindCoreUntyped | MethodsToGen_CoreBindingHelper.GetCore) ||
if (emitAsConfigWithChildren)
{
EmitBlankLineIfRequired();
EmitAsConfigWithChildrenMethod();
}
if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCoreMain | MethodsToGen_CoreBindingHelper.GetCore) ||
ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions))
{
_writer.WriteLine();
EmitBlankLineIfRequired();
EmitGetBinderOptionsHelper();
_emitBlankLineBeforeNextStatement = true;
}
bool enumTypeExists = false;
@ -458,17 +442,17 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
EmitBlankLineIfRequired();
_writer.WriteLine($$"""
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void {{Identifier.ValidateConfigurationKeys}}(Type {{Identifier.type}}, {{MinimalDisplayString.LazyHashSetOfString}} {{keysIdentifier}}, {{Identifier.IConfiguration}} {{Identifier.configuration}}, {{Identifier.BinderOptions}}? {{Identifier.binderOptions}})
public static void {{Identifier.ValidateConfigurationKeys}}(Type {{Identifier.type}}, {{TypeDisplayString.LazyHashSetOfString}} {{keysIdentifier}}, {{Identifier.IConfiguration}} {{Identifier.configuration}}, {{Identifier.BinderOptions}}? {{Identifier.binderOptions}})
{
if ({{Identifier.binderOptions}}?.{{Identifier.ErrorOnUnknownConfiguration}} is true)
{
{{MinimalDisplayString.ListOfString}}? {{Identifier.temp}} = null;
{{TypeDisplayString.ListOfString}}? {{Identifier.temp}} = null;
foreach ({{Identifier.IConfigurationSection}} {{Identifier.section}} in {{Identifier.configuration}}.{{Identifier.GetChildren}}())
{
if (!{{keysIdentifier}}.Value.Contains({{Expression.sectionKey}}))
{
({{Identifier.temp}} ??= new {{MinimalDisplayString.ListOfString}}()).Add($"'{{{Expression.sectionKey}}}'");
({{Identifier.temp}} ??= new {{TypeDisplayString.ListOfString}}()).Add($"'{{{Expression.sectionKey}}}'");
}
}
@ -490,7 +474,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return true;
}
return {{Identifier.AsConfigWithChildren}}({{Identifier.configuration}}) is not null;
return {{MethodsToGen_CoreBindingHelper.AsConfigWithChildren}}({{Identifier.configuration}}) is not null;
}
""");
}
@ -498,7 +482,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
private void EmitAsConfigWithChildrenMethod()
{
_writer.WriteLine($$"""
public static {{Identifier.IConfiguration}}? {{Identifier.AsConfigWithChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
public static {{Identifier.IConfiguration}}? {{MethodsToGen_CoreBindingHelper.AsConfigWithChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
{
foreach ({{Identifier.IConfigurationSection}} _ in {{Identifier.configuration}}.{{Identifier.GetChildren}}())
{
@ -512,7 +496,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
private void EmitGetBinderOptionsHelper()
{
_writer.WriteLine($$"""
public static {{Identifier.BinderOptions}}? {{Identifier.GetBinderOptions}}({{MinimalDisplayString.NullableActionOfBinderOptions}} {{Identifier.configureOptions}})
public static {{Identifier.BinderOptions}}? {{Identifier.GetBinderOptions}}({{TypeDisplayString.NullableActionOfBinderOptions}} {{Identifier.configureOptions}})
{
if ({{Identifier.configureOptions}} is null)
{
@ -524,7 +508,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if ({{Identifier.binderOptions}}.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"{{string.Format(ExceptionMessages.CannotSpecifyBindNonPublicProperties)}}");
throw new NotSupportedException($"{{string.Format(ExceptionMessages.CannotSpecifyBindNonPublicProperties)}}");
}
return {{Identifier.binderOptions}};
@ -534,7 +518,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
private void EmitEnumParseMethod()
{
string innerExceptionTypeDisplayString = _useFullyQualifiedNames ? "global::System.Exception" : "Exception";
string exceptionArg1 = string.Format(ExceptionMessages.FailedBinding, $"{{{Identifier.getPath}()}}", $"{{typeof(T)}}");
_writer.WriteLine($$"""
@ -548,9 +531,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return Enum.Parse<T>(value, ignoreCase: true);
#endif
}
catch ({{innerExceptionTypeDisplayString}} {{Identifier.exception}})
catch ({{Identifier.Exception}} {{Identifier.exception}})
{
throw new {{GetInvalidOperationDisplayName()}}($"{{exceptionArg1}}", {{Identifier.exception}});
throw new {{Identifier.InvalidOperationException}}($"{{exceptionArg1}}", {{Identifier.exception}});
}
}
""");
@ -558,29 +541,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
private void EmitPrimitiveParseMethod(ParsableFromStringSpec type)
{
string innerExceptionTypeDisplayString;
string cultureInfoTypeDisplayString;
string numberStylesTypeDisplayString;
if (_useFullyQualifiedNames)
{
innerExceptionTypeDisplayString = "global::System.Exception";
cultureInfoTypeDisplayString = "global::System.Globalization.CultureInfo";
numberStylesTypeDisplayString = "global::System.Globalization.NumberStyles";
}
else
{
innerExceptionTypeDisplayString = "Exception";
cultureInfoTypeDisplayString = "CultureInfo";
numberStylesTypeDisplayString = "NumberStyles";
}
StringParsableTypeKind typeKind = type.StringParsableTypeKind;
string typeDisplayString = type.MinimalDisplayString;
string invariantCultureExpression = $"{cultureInfoTypeDisplayString}.InvariantCulture";
string typeDisplayString = type.DisplayString;
string invariantCultureExpression = $"{Identifier.CultureInfo}.InvariantCulture";
string parsedValueExpr;
switch (typeKind)
{
case StringParsableTypeKind.Enum:
@ -592,12 +558,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
break;
case StringParsableTypeKind.Integer:
{
parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {numberStylesTypeDisplayString}.Integer, {invariantCultureExpression})";
parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {Identifier.NumberStyles}.Integer, {invariantCultureExpression})";
}
break;
case StringParsableTypeKind.Float:
{
parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {numberStylesTypeDisplayString}.Float, {invariantCultureExpression})";
parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {Identifier.NumberStyles}.Float, {invariantCultureExpression})";
}
break;
case StringParsableTypeKind.Parse:
@ -612,7 +578,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
break;
case StringParsableTypeKind.CultureInfo:
{
parsedValueExpr = $"{cultureInfoTypeDisplayString}.GetCultureInfo({Identifier.value})";
parsedValueExpr = $"{Identifier.CultureInfo}.GetCultureInfo({Identifier.value})";
}
break;
case StringParsableTypeKind.Uri:
@ -635,9 +601,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return {{parsedValueExpr}};
}
catch ({{innerExceptionTypeDisplayString}} {{Identifier.exception}})
catch ({{Identifier.Exception}} {{Identifier.exception}})
{
throw new {{GetInvalidOperationDisplayName()}}($"{{exceptionArg1}}", {{Identifier.exception}});
throw new {{Identifier.InvalidOperationException}}($"{{exceptionArg1}}", {{Identifier.exception}});
}
""");
}
@ -722,13 +688,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (keyType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue)
{
// Save value to local to avoid parsing twice - during look-up and during add.
_writer.WriteLine($"{keyType.MinimalDisplayString} {Identifier.key} = {parsedKeyExpr};");
_writer.WriteLine($"{keyType.DisplayString} {Identifier.key} = {parsedKeyExpr};");
parsedKeyExpr = Identifier.key;
}
bool isValueType = elementType.IsValueType;
string expressionForElementIsNotNull = $"{Identifier.element} is not null";
string elementTypeDisplayString = elementType.MinimalDisplayString + (elementType.IsValueType ? string.Empty : "?");
string elementTypeDisplayString = elementType.DisplayString + (elementType.IsValueType ? string.Empty : "?");
string expressionForElementExists = $"{objIdentifier}.{Identifier.TryGetValue}({parsedKeyExpr}, out {elementTypeDisplayString} {Identifier.element})";
string conditionToUseExistingElement = expressionForElementExists;
@ -749,7 +715,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
// we need to copy its contents into a new instance & then append/bind to that.
string initExpression = collectionSpec.InitializationStrategy is InitializationStrategy.ParameterizedConstructor
? $"new {collectionSpec.ConcreteType.MinimalDisplayString}({Identifier.element})"
? $"new {collectionSpec.ConcreteType.DisplayString}({Identifier.element})"
: $"{Identifier.element}.{collectionSpec.ToEnumerableMethodCall!}";
_writer.WriteLine($$"""
@ -773,7 +739,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
Debug.Assert(type.NeedsMemberBinding);
string keyCacheFieldName = GetConfigKeyCacheFieldName(type);
string validateMethodCallExpr = $"{Identifier.ValidateConfigurationKeys}(typeof({type.MinimalDisplayString}), {keyCacheFieldName}, {Identifier.configuration}, {Identifier.binderOptions});";
string validateMethodCallExpr = $"{Identifier.ValidateConfigurationKeys}(typeof({type.DisplayString}), {keyCacheFieldName}, {Identifier.configuration}, {Identifier.binderOptions});";
_writer.WriteLine(validateMethodCallExpr);
foreach (PropertySpec property in type.Properties.Values)
@ -781,7 +747,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
bool noSetter_And_IsReadonly = !property.CanSet && property.Type is CollectionSpec { InitializationStrategy: InitializationStrategy.ParameterizedConstructor };
if (property.ShouldBind() && !noSetter_And_IsReadonly)
{
string containingTypeRef = property.IsStatic ? type.MinimalDisplayString : Identifier.obj;
string containingTypeRef = property.IsStatic ? type.DisplayString : Identifier.obj;
EmitBindImplForMember(
property,
memberAccessExpr: $"{containingTypeRef}.{property.Name}",
@ -832,7 +798,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return true;
}
string sectionValidationCall = $"{Identifier.AsConfigWithChildren}({sectionParseExpr})";
string sectionValidationCall = $"{MethodsToGen_CoreBindingHelper.AsConfigWithChildren}({sectionParseExpr})";
string sectionIdentifier = GetIncrementalIdentifier(Identifier.section);
EmitStartBlock($"if ({sectionValidationCall} is {Identifier.IConfigurationSection} {sectionIdentifier})");
@ -869,14 +835,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
Debug.Assert(canSet);
string effectiveMemberTypeDisplayString = effectiveMemberType.MinimalDisplayString;
string effectiveMemberTypeDisplayString = effectiveMemberType.DisplayString;
initKind = InitializationKind.None;
if (memberType.SpecKind is TypeSpecKind.Nullable)
{
string nullableTempIdentifier = GetIncrementalIdentifier(Identifier.temp);
_writer.WriteLine($"{memberType.MinimalDisplayString} {nullableTempIdentifier} = {memberAccessExpr};");
_writer.WriteLine($"{memberType.DisplayString} {nullableTempIdentifier} = {memberAccessExpr};");
_writer.WriteLine(
$"{effectiveMemberTypeDisplayString} {tempIdentifier} = {nullableTempIdentifier}.{Identifier.HasValue} ? {nullableTempIdentifier}.{Identifier.Value} : new {effectiveMemberTypeDisplayString}();");
@ -924,7 +890,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
objIdentifier = Identifier.temp;
_writer.WriteLine($$"""
if ({{Identifier.obj}} is not {{type.PopulationCastType!.MinimalDisplayString}} {{objIdentifier}})
if ({{Identifier.obj}} is not {{type.PopulationCastType!.DisplayString}} {{objIdentifier}})
{
return;
}
@ -936,20 +902,31 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
private void Emit_Foreach_Section_In_ConfigChildren_StartBlock() =>
EmitStartBlock($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())");
private void Emit_NotSupportedException_TypeNotDetectedAsInput() =>
_writer.WriteLine(@$"throw new NotSupportedException($""{string.Format(ExceptionMessages.TypeNotDetectedAsInput, "{type}")}"");");
private static string GetSectionPathFromConfigurationExpression(string configurationKeyName)
=> $@"{GetSectionFromConfigurationExpression(configurationKeyName)}.{Identifier.Path}";
private static string GetSectionFromConfigurationExpression(string configurationKeyName, bool addQuotes = true)
{
string argExpr = addQuotes ? $@"""{configurationKeyName}""" : configurationKeyName;
return $@"{Expression.configurationGetSection}({argExpr})";
return $@"{Identifier.configuration}.{Identifier.GetSection}({argExpr})";
}
private static string GetConditionKindExpr(ref bool isFirstType)
{
if (isFirstType)
{
isFirstType = false;
return "if";
}
return "else if";
}
private static string GetConfigKeyCacheFieldName(ObjectSpec type) =>
$"s_configKeys_{type.DisplayStringWithoutSpecialCharacters}";
private void Emit_NotSupportedException_TypeNotDetectedAsInput() =>
_writer.WriteLine(@$"throw new global::System.NotSupportedException($""{string.Format(ExceptionMessages.TypeNotDetectedAsInput, "{type}")}"");");
$"s_configKeys_{type.DisplayString.ToIdentifierSubstring()}";
}
}
}

View file

@ -1,6 +1,8 @@
// 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 System.Diagnostics;
using System.Reflection;
@ -10,7 +12,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
private sealed partial class Emitter
{
private static readonly AssemblyName s_assemblyName = typeof(Emitter).Assembly.GetName();
private string? _emittedExtsTargetType;
internal static readonly AssemblyName s_assemblyName = typeof(ConfigurationBindingGenerator).Assembly.GetName();
private enum InitializationKind
{
@ -26,29 +30,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public const string sectionPath = "section.Path";
public const string sectionValue = "section.Value";
public const string GetBinderOptions = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{Identifier.GetBinderOptions}";
public static string GeneratedCodeAnnotation = $@"[GeneratedCode(""{s_assemblyName.Name}"", ""{s_assemblyName.Version}"")]";
}
private static class FullyQualifiedDisplayString
{
public const string ActionOfBinderOptions = $"global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>";
public const string AddSingleton = $"{ServiceCollectionServiceExtensions}.AddSingleton";
public const string ConfigurationChangeTokenSource = "global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource";
public const string CoreBindingHelper = $"global::{ProjectName}.{Identifier.CoreBindingHelper}";
public const string IConfiguration = "global::Microsoft.Extensions.Configuration.IConfiguration";
public const string IConfigurationSection = IConfiguration + "Section";
public const string IOptionsChangeTokenSource = "global::Microsoft.Extensions.Options.IOptionsChangeTokenSource";
public const string InvalidOperationException = "global::System.InvalidOperationException";
public const string IServiceCollection = "global::Microsoft.Extensions.DependencyInjection.IServiceCollection";
public const string NotSupportedException = "global::System.NotSupportedException";
public const string OptionsBuilderOfTOptions = $"global::Microsoft.Extensions.Options.OptionsBuilder<{Identifier.TOptions}>";
public const string ServiceCollectionServiceExtensions = "global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions";
public const string Type = $"global::System.Type";
}
private static class MinimalDisplayString
private static class TypeDisplayString
{
public const string NullableActionOfBinderOptions = "Action<BinderOptions>?";
public const string OptionsBuilderOfTOptions = $"OptionsBuilder<{Identifier.TOptions}>";
public const string HashSetOfString = "HashSet<string>";
public const string LazyHashSetOfString = "Lazy<HashSet<string>>";
public const string ListOfString = "List<string>";
@ -75,6 +63,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public const string services = nameof(services);
public const string temp = nameof(temp);
public const string type = nameof(type);
public const string typedObj = nameof(typedObj);
public const string validateKeys = nameof(validateKeys);
public const string value = nameof(value);
@ -82,21 +71,19 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public const string AddSingleton = nameof(AddSingleton);
public const string Any = nameof(Any);
public const string Array = nameof(Array);
public const string AsConfigWithChildren = nameof(AsConfigWithChildren);
public const string Bind = nameof(Bind);
public const string BinderOptions = nameof(BinderOptions);
public const string BindingExtensions = nameof(BindingExtensions);
public const string ConfigurationChangeTokenSource = nameof(ConfigurationChangeTokenSource);
public const string Configure = nameof(Configure);
public const string CopyTo = nameof(CopyTo);
public const string ContainsKey = nameof(ContainsKey);
public const string CoreBindingHelper = nameof(CoreBindingHelper);
public const string Count = nameof(Count);
public const string CultureInfo = nameof(CultureInfo);
public const string CultureNotFoundException = nameof(CultureNotFoundException);
public const string Enum = nameof(Enum);
public const string ErrorOnUnknownConfiguration = nameof(ErrorOnUnknownConfiguration);
public const string GeneratedConfigurationBinder = nameof(GeneratedConfigurationBinder);
public const string GeneratedOptionsBuilderBinder = nameof(GeneratedOptionsBuilderBinder);
public const string GeneratedServiceCollectionBinder = nameof(GeneratedServiceCollectionBinder);
public const string Exception = nameof(Exception);
public const string Get = nameof(Get);
public const string GetBinderOptions = nameof(GetBinderOptions);
public const string GetChildren = nameof(GetChildren);
@ -108,9 +95,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public const string IConfiguration = nameof(IConfiguration);
public const string IConfigurationSection = nameof(IConfigurationSection);
public const string Int32 = "int";
public const string InterceptsLocation = nameof(InterceptsLocation);
public const string InvalidOperationException = nameof(InvalidOperationException);
public const string InvariantCulture = nameof(InvariantCulture);
public const string IOptionsChangeTokenSource = nameof(IOptionsChangeTokenSource);
public const string IServiceCollection = nameof(IServiceCollection);
public const string Length = nameof(Length);
public const string NumberStyles = nameof(NumberStyles);
public const string Parse = nameof(Parse);
public const string Path = nameof(Path);
public const string Resize = nameof(Resize);
@ -118,17 +109,66 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public const string TOptions = nameof(TOptions);
public const string TryCreate = nameof(TryCreate);
public const string TryGetValue = nameof(TryGetValue);
public const string TryParse = nameof(TryParse);
public const string Type = nameof(Type);
public const string Uri = nameof(Uri);
public const string ValidateConfigurationKeys = nameof(ValidateConfigurationKeys);
public const string Value = nameof(Value);
}
private bool ShouldEmitBinders() =>
private bool ShouldEmitBindingExtensions() =>
ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Any) ||
ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Any) ||
ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Any);
private void EmitInterceptsLocationAnnotations(Enum generatedBindingOverload)
{
// The only time a generated binding method won't have any locations to
// intercept is when either of these methods are used as helpers for
// other generated OptionsBuilder or ServiceCollection binding extensions.
bool interceptsCalls = _sourceGenSpec.InterceptionInfo.TryGetValue(generatedBindingOverload, out List<InterceptorLocationInfo>? infoList);
Debug.Assert(interceptsCalls ||
generatedBindingOverload is MethodsToGen_Extensions_ServiceCollection.Configure_T_name_BinderOptions ||
generatedBindingOverload is MethodsToGen_Extensions_OptionsBuilder.Bind_T_BinderOptions);
if (interceptsCalls)
{
EmitInterceptsLocationAnnotations(infoList);
}
}
private void EmitInterceptsLocationAnnotations(List<InterceptorLocationInfo> infoList)
{
foreach (InterceptorLocationInfo info in infoList)
{
_writer.WriteLine($@"[{Identifier.InterceptsLocation}Attribute(@""{info.FilePath}"", {info.LineNumber}, {info.CharacterNumber})]");
}
}
private void EmitBindingExtStartRegion(string targetType)
{
Debug.Assert(_emittedExtsTargetType is null);
EmitBlankLineIfRequired();
_emittedExtsTargetType = targetType;
EmitBindingExtRegionText(isStart: true);
_emitBlankLineBeforeNextStatement = false;
}
private void EmitBindingExtEndRegion()
{
Debug.Assert(_emittedExtsTargetType is not null);
EmitBindingExtRegionText(isStart: false);
_emittedExtsTargetType = null;
_emitBlankLineBeforeNextStatement = true;
}
private void EmitBindingExtRegionText(bool isStart)
{
string endSource = isStart ? string.Empty : "end";
_writer.WriteLine($"#{endSource}region {_emittedExtsTargetType} extensions.");
}
/// <summary>
/// Starts a block of source code.
/// </summary>
@ -171,24 +211,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
_emitBlankLineBeforeNextStatement = true;
}
private void EmitCheckForNullArgument_WithBlankLine_IfRequired(bool isValueType)
{
if (!isValueType)
{
EmitCheckForNullArgument_WithBlankLine(Identifier.obj);
}
}
private void EmitCheckForNullArgument_WithBlankLine(string paramName)
{
string exceptionTypeDisplayString = _useFullyQualifiedNames
? "global::System.ArgumentNullException"
: "ArgumentNullException";
_writer.WriteLine($$"""
if ({{paramName}} is null)
{
throw new {{exceptionTypeDisplayString}}(nameof({{paramName}}));
throw new ArgumentNullException(nameof({{paramName}}));
}
""");
@ -201,51 +229,28 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (!type.CanInitialize)
{
_writer.WriteLine(GetInitException(type.InitExceptionMessage) + ";");
_writer.WriteLine($@"throw new {Identifier.InvalidOperationException}(""{type.InitExceptionMessage}"");");
return true;
}
return false;
}
private void EmitRootBindingClassStartBlock(string className)
{
EmitBlankLineIfRequired();
EmitStartBlock($$"""
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
{{GetGeneratedCodeAttributeSrc()}}
internal static class {{className}}
""");
_emitBlankLineBeforeNextStatement = false;
}
private string GetGeneratedCodeAttributeSrc()
{
string attributeRefExpr = _useFullyQualifiedNames ? $"global::System.CodeDom.Compiler.GeneratedCodeAttribute" : "GeneratedCode";
return $@"[{attributeRefExpr}(""{s_assemblyName.Name}"", ""{s_assemblyName.Version}"")]";
}
private string GetInitException(string message) => $@"throw new {GetInvalidOperationDisplayName()}(""{message}"")";
private string GetIncrementalIdentifier(string prefix) => $"{prefix}{_valueSuffixIndex++}";
private string GetInitalizeMethodDisplayString(ObjectSpec type) =>
GetHelperMethodDisplayString($"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.DisplayStringWithoutSpecialCharacters}");
private static string GetInitalizeMethodDisplayString(ObjectSpec type) =>
$"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.DisplayString.ToIdentifierSubstring()}";
}
}
private string GetTypeDisplayString(TypeSpec type) => _useFullyQualifiedNames ? type.FullyQualifiedDisplayString : type.MinimalDisplayString;
private string GetHelperMethodDisplayString(string methodName)
internal static class EmitterExtensions
{
if (_useFullyQualifiedNames)
{
methodName = FullyQualifiedDisplayString.CoreBindingHelper + "." + methodName;
}
return methodName;
}
private string GetInvalidOperationDisplayName() => _useFullyQualifiedNames ? FullyQualifiedDisplayString.InvalidOperationException : Identifier.InvalidOperationException;
}
public static string ToIdentifierSubstring(this string typeDisplayName) =>
typeDisplayName
.Replace("[]", nameof(Array))
.Replace(", ", string.Empty)
.Replace(".", string.Empty)
.Replace("<", string.Empty)
.Replace(">", string.Empty);
}
}

View file

@ -9,19 +9,17 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
private bool ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder methods) => (_sourceGenSpec.MethodsToGen_OptionsBuilderExt & methods) != 0;
private void EmitBinder_Extensions_OptionsBuilder()
private void EmitBindingExtensions_OptionsBuilder()
{
if (!ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Any))
{
return;
}
EmitRootBindingClassStartBlock(Identifier.GeneratedOptionsBuilderBinder);
EmitBindingExtStartRegion(TypeDisplayString.OptionsBuilderOfTOptions);
EmitBindMethods_Extensions_OptionsBuilder();
EmitBindConfigurationMethod();
EmitEndBlock();
EmitBindingExtEndRegion();
}
private void EmitBindMethods_Extensions_OptionsBuilder()
@ -32,24 +30,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
const string documentation = @"/// <summary>Registers a configuration instance which <typeparamref name=""TOptions""/> will bind against.</summary>";
const string paramList = $"{FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}";
const string paramList = $"{Identifier.IConfiguration} {Identifier.configuration}";
if (ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Bind_T))
{
EmitMethodStartBlock("Bind", paramList, documentation);
_writer.WriteLine($"return global::{Identifier.GeneratedOptionsBuilderBinder}.Bind({Identifier.optionsBuilder}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
EmitMethodStartBlock(MethodsToGen_Extensions_OptionsBuilder.Bind_T, "Bind", paramList, documentation);
_writer.WriteLine($"return Bind({Identifier.optionsBuilder}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
EmitEndBlock();
}
EmitMethodStartBlock(
MethodsToGen_Extensions_OptionsBuilder.Bind_T_BinderOptions,
"Bind",
paramList + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}",
paramList + $", {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}",
documentation);
EmitCheckForNullArgument_WithBlankLine(Identifier.optionsBuilder);
_writer.WriteLine($$"""
global::{{Identifier.GeneratedServiceCollectionBinder}}.{{Identifier.Configure}}<{{Identifier.TOptions}}>({{Identifier.optionsBuilder}}.{{Identifier.Services}}, {{Identifier.optionsBuilder}}.Name, {{Identifier.configuration}}, {{Identifier.configureOptions}});
{{Identifier.Configure}}<{{Identifier.TOptions}}>({{Identifier.optionsBuilder}}.{{Identifier.Services}}, {{Identifier.optionsBuilder}}.Name, {{Identifier.configuration}}, {{Identifier.configureOptions}});
return {{Identifier.optionsBuilder}};
""");
@ -63,19 +62,20 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
const string documentation = $@"/// <summary>Registers the dependency injection container to bind <typeparamref name=""TOptions""/> against the <see cref=""{FullyQualifiedDisplayString.IConfiguration}""/> obtained from the DI service provider.</summary>";
string paramList = $"string {Identifier.configSectionPath}, {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions} = null";
const string documentation = $@"/// <summary>Registers the dependency injection container to bind <typeparamref name=""TOptions""/> against the <see cref=""{Identifier.IConfiguration}""/> obtained from the DI service provider.</summary>";
string paramList = $"string {Identifier.configSectionPath}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions} = null";
EmitMethodStartBlock("BindConfiguration", paramList, documentation);
EmitMethodStartBlock(MethodsToGen_Extensions_OptionsBuilder.BindConfiguration, "BindConfiguration", paramList, documentation);
EmitCheckForNullArgument_WithBlankLine(Identifier.optionsBuilder);
EmitCheckForNullArgument_WithBlankLine(Identifier.configSectionPath);
EmitStartBlock($"{Identifier.optionsBuilder}.{Identifier.Configure}<{FullyQualifiedDisplayString.IConfiguration}>(({Identifier.obj}, {Identifier.configuration}) =>");
EmitStartBlock($"{Identifier.optionsBuilder}.{Identifier.Configure}<{Identifier.IConfiguration}>(({Identifier.obj}, {Identifier.configuration}) =>");
EmitCheckForNullArgument_WithBlankLine(Identifier.obj);
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
_writer.WriteLine($$"""
{{FullyQualifiedDisplayString.IConfiguration}} {{Identifier.section}} = string.Equals(string.Empty, {{Identifier.configSectionPath}}, global::System.StringComparison.OrdinalIgnoreCase) ? {{Identifier.configuration}} : {{Identifier.configuration}}.{{Identifier.GetSection}}({{Identifier.configSectionPath}});
{{FullyQualifiedDisplayString.CoreBindingHelper}}.{{nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}}({{Identifier.section}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}});
{{Identifier.IConfiguration}} {{Identifier.section}} = string.Equals(string.Empty, {{Identifier.configSectionPath}}, StringComparison.OrdinalIgnoreCase) ? {{Identifier.configuration}} : {{Identifier.configuration}}.{{Identifier.GetSection}}({{Identifier.configSectionPath}});
{{nameof(MethodsToGen_CoreBindingHelper.BindCoreMain)}}({{Identifier.section}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}});
""");
EmitEndBlock(endBraceTrailingSource: ");");
@ -83,20 +83,20 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
_writer.WriteLine();
_writer.WriteLine($$"""
{{FullyQualifiedDisplayString.AddSingleton}}<{{FullyQualifiedDisplayString.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>, {{FullyQualifiedDisplayString.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>>({{Identifier.optionsBuilder}}.{{Identifier.Services}});
{{Identifier.optionsBuilder}}.{{Identifier.Services}}.{{Identifier.AddSingleton}}<{{Identifier.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>, {{Identifier.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>>();
return {{Identifier.optionsBuilder}};
""");
EmitEndBlock();
}
private void EmitMethodStartBlock(string methodName, string paramList, string documentation)
private void EmitMethodStartBlock(MethodsToGen_Extensions_OptionsBuilder method, string methodName, string paramList, string documentation)
{
paramList = $"this {FullyQualifiedDisplayString.OptionsBuilderOfTOptions} {Identifier.optionsBuilder}, {paramList}";
paramList = $"this {TypeDisplayString.OptionsBuilderOfTOptions} {Identifier.optionsBuilder}, {paramList}";
EmitBlankLineIfRequired();
_writer.WriteLine(documentation);
EmitStartBlock($"public static {FullyQualifiedDisplayString.OptionsBuilderOfTOptions} {methodName}<{Identifier.TOptions}>({paramList}) where {Identifier.TOptions} : class");
EmitInterceptsLocationAnnotations(method);
EmitStartBlock($"public static {TypeDisplayString.OptionsBuilderOfTOptions} {methodName}<{Identifier.TOptions}>({paramList}) where {Identifier.TOptions} : class");
}
}
}

View file

@ -9,71 +9,72 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
private bool ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection methods) => (_sourceGenSpec.MethodsToGen_ServiceCollectionExt & methods) != 0;
private void EmitBinder_Extensions_IServiceCollection()
private void EmitBindingExtensions_IServiceCollection()
{
if (!ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Any))
{
return;
}
EmitRootBindingClassStartBlock(Identifier.GeneratedServiceCollectionBinder);
EmitBindingExtStartRegion(Identifier.IServiceCollection);
EmitConfigureMethods();
EmitBindingExtEndRegion();
}
private void EmitConfigureMethods()
{
const string defaultNameExpr = "string.Empty";
const string configureMethodString = $"global::{Identifier.GeneratedServiceCollectionBinder}.{Identifier.Configure}";
string configParam = $"{FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}";
string configParam = $"{Identifier.IConfiguration} {Identifier.configuration}";
if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T))
{
EmitStartMethod(configParam);
_writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
EmitStartMethod(MethodsToGen_Extensions_ServiceCollection.Configure_T, configParam);
_writer.WriteLine($"return {Identifier.Configure}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
EmitEndBlock();
}
if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T_name))
{
EmitStartMethod(
MethodsToGen_Extensions_ServiceCollection.Configure_T_name,
paramList: $"string? {Identifier.name}, " + configParam);
_writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {Identifier.name}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
_writer.WriteLine($"return {Identifier.Configure}<{Identifier.TOptions}>({Identifier.services}, {Identifier.name}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
EmitEndBlock();
}
if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T_BinderOptions))
{
EmitStartMethod(
paramList: configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}");
_writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions});");
MethodsToGen_Extensions_ServiceCollection.Configure_T_BinderOptions,
paramList: configParam + $", {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}");
_writer.WriteLine($"return {Identifier.Configure}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions});");
EmitEndBlock();
}
// Core Configure method that the other overloads call.
// Like the others, it is public API that could be called directly by users.
// So, it is always generated whenever a Configure overload is called.
string optionsNamespaceName = "global::Microsoft.Extensions.Options";
string bindCoreUntypedDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped));
string optionsNamespaceName = "Microsoft.Extensions.Options";
EmitStartMethod(paramList: $"string? {Identifier.name}, " + configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}");
EmitStartMethod(MethodsToGen_Extensions_ServiceCollection.Configure_T_name_BinderOptions, paramList: $"string? {Identifier.name}, " + configParam + $", {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}");
EmitCheckForNullArgument_WithBlankLine(Identifier.services);
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
_writer.WriteLine($$"""
global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions({{Identifier.services}});
{{FullyQualifiedDisplayString.AddSingleton}}<{{FullyQualifiedDisplayString.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>>({{Identifier.services}}, new {{FullyQualifiedDisplayString.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.configuration}}));
return {{FullyQualifiedDisplayString.AddSingleton}}<{{optionsNamespaceName}}.IConfigureOptions<{{Identifier.TOptions}}>>({{Identifier.services}}, new {{optionsNamespaceName}}.ConfigureNamedOptions<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.obj}} => {{bindCoreUntypedDisplayString}}({{Identifier.configuration}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}})));
OptionsServiceCollectionExtensions.AddOptions({{Identifier.services}});
{{Identifier.services}}.{{Identifier.AddSingleton}}<{{Identifier.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>>(new {{Identifier.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.configuration}}));
return {{Identifier.services}}.{{Identifier.AddSingleton}}<{{optionsNamespaceName}}.IConfigureOptions<{{Identifier.TOptions}}>>(new {{optionsNamespaceName}}.ConfigureNamedOptions<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.obj}} => {{nameof(MethodsToGen_CoreBindingHelper.BindCoreMain)}}({{Identifier.configuration}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}){{Identifier.configureOptions}})));
""");
EmitEndBlock();
EmitEndBlock();
_emitBlankLineBeforeNextStatement = true;
}
private void EmitStartMethod(string paramList)
private void EmitStartMethod(MethodsToGen_Extensions_ServiceCollection overload, string paramList)
{
paramList = $"this {FullyQualifiedDisplayString.IServiceCollection} {Identifier.services}, {paramList}";
paramList = $"this {Identifier.IServiceCollection} {Identifier.services}, {paramList}";
EmitBlankLineIfRequired();
EmitStartBlock($$"""
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static {{FullyQualifiedDisplayString.IServiceCollection}} {{Identifier.Configure}}<{{Identifier.TOptions}}>({{paramList}}) where {{Identifier.TOptions}} : class
""");
_writer.WriteLine("/// <summary>Registers a configuration instance which TOptions will bind against.</summary>");
EmitInterceptsLocationAnnotations(overload);
EmitStartBlock($"public static {Identifier.IServiceCollection} {Identifier.Configure}<{Identifier.TOptions}>({paramList}) where {Identifier.TOptions} : class");
}
}
}

View file

@ -0,0 +1,90 @@
// 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;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal sealed record InterceptorLocationInfo
{
public InterceptorLocationInfo(IInvocationOperation operation)
{
SyntaxNode operationSyntax = operation.Syntax;
TextSpan operationSpan = operationSyntax.Span;
SyntaxTree operationSyntaxTree = operationSyntax.SyntaxTree;
FilePath = GetInterceptorFilePath(operationSyntaxTree, operation.SemanticModel?.Compilation.Options.SourceReferenceResolver);
FileLinePositionSpan span = operationSyntaxTree.GetLineSpan(operationSpan);
LineNumber = span.StartLinePosition.Line + 1;
// Calculate the character offset to the end of the binding invocation detected.
int invocationLength = ((MemberAccessExpressionSyntax)((InvocationExpressionSyntax)operationSyntax).Expression).Expression.Span.Length;
CharacterNumber = span.StartLinePosition.Character + invocationLength + 2;
}
public string FilePath { get; }
public int LineNumber { get; }
public int CharacterNumber { get; }
// Utilize the same logic used by the interceptors API for resolving the source mapped value of a path.
// https://github.com/dotnet/roslyn/blob/f290437fcc75dad50a38c09e0977cce13a64f5ba/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs#L1063-L1064
private static string GetInterceptorFilePath(SyntaxTree tree, SourceReferenceResolver? resolver) =>
resolver?.NormalizePath(tree.FilePath, baseFilePath: null) ?? tree.FilePath;
}
internal sealed record ConfigurationBinderInterceptorInfo
{
private OverloadInterceptorInfo? _bind_Instance;
private OverloadInterceptorInfo? _bind_instance_BinderOptions;
private OverloadInterceptorInfo? _bind_key_instance;
public void RegisterOverloadInfo(MethodsToGen_ConfigurationBinder overload, TypeSpec type, IInvocationOperation operation)
{
OverloadInterceptorInfo overloadInfo = DetermineOverload(overload, initIfNull: true);
overloadInfo.RegisterLocationInfo(type, operation);
}
public OverloadInterceptorInfo GetOverloadInfo(MethodsToGen_ConfigurationBinder overload) =>
DetermineOverload(overload, initIfNull: false) ?? throw new ArgumentNullException(nameof(overload));
private OverloadInterceptorInfo? DetermineOverload(MethodsToGen_ConfigurationBinder overload, bool initIfNull)
{
return overload switch
{
MethodsToGen_ConfigurationBinder.Bind_instance => InitIfNull(ref _bind_Instance),
MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions => InitIfNull(ref _bind_instance_BinderOptions),
MethodsToGen_ConfigurationBinder.Bind_key_instance => InitIfNull(ref _bind_key_instance),
_ => throw new InvalidOperationException(nameof(overload)),
};
OverloadInterceptorInfo InitIfNull(ref OverloadInterceptorInfo? info)
{
if (initIfNull)
{
info ??= new OverloadInterceptorInfo();
}
return info;
}
}
}
internal sealed record OverloadInterceptorInfo : IEnumerable<KeyValuePair<TypeSpec, List<InterceptorLocationInfo>>>
{
private readonly Dictionary<TypeSpec, List<InterceptorLocationInfo>> _typeInterceptionInfo = new();
public void RegisterLocationInfo(TypeSpec type, IInvocationOperation operation) =>
_typeInterceptionInfo.RegisterCacheEntry(type, new InterceptorLocationInfo(operation));
public IEnumerator<KeyValuePair<TypeSpec, List<InterceptorLocationInfo>>> GetEnumerator() => _typeInterceptionInfo.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

View file

@ -10,11 +10,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
None = 0x0,
BindCore = 0x1,
BindCoreUntyped = 0x2,
GetCore = 0x4,
GetValueCore = 0x8,
GetCore = 0x2,
GetValueCore = 0x4,
BindCoreMain = 0x8,
Initialize = 0x10,
AsConfigWithChildren = 0x20,
HasValueOrChildren = 0x20,
AsConfigWithChildren = 0x40,
}
/// <summary>

View file

@ -40,7 +40,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
IsValidMethodName_OptionsConfigurationServiceCollectionExtensions(name);
}
private static bool IsBindingOperation(IInvocationOperation operation)
public static bool IsBindingOperation(IInvocationOperation operation)
{
if (operation.TargetMethod is not IMethodSymbol
{

View file

@ -102,15 +102,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (GetTargetTypeForRootInvocationCore(type, invocation.Location) is TypeSpec typeSpec)
{
Dictionary<MethodsToGen_ConfigurationBinder, HashSet<TypeSpec>> types = _sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods;
if (!types.TryGetValue(overload, out HashSet<TypeSpec>? typeSpecs))
{
types[overload] = typeSpecs = new HashSet<TypeSpec>();
}
_sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload;
typeSpecs.Add(typeSpec);
RegisterAsInterceptor_ConfigBinder_BindMethod(overload, typeSpec, invocation.Operation);
}
static ITypeSymbol? ResolveType(IOperation conversionOperation) =>
@ -184,8 +176,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (GetTargetTypeForRootInvocation(type, invocation.Location) is TypeSpec typeSpec)
{
_sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload;
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetCore, typeSpec);
RegisterAsInterceptor_ConfigBinder(overload, invocation.Operation);
RegisterTypeForGetCoreGen(typeSpec);
}
}
@ -253,10 +245,27 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (IsParsableFromString(effectiveType, out _) &&
GetTargetTypeForRootInvocationCore(type, invocation.Location) is TypeSpec typeSpec)
{
_sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload;
RegisterAsInterceptor_ConfigBinder(overload, invocation.Operation);
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetValueCore, typeSpec);
}
}
private void RegisterAsInterceptor_ConfigBinder(MethodsToGen_ConfigurationBinder overload, IInvocationOperation operation)
{
_sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload;
RegisterAsInterceptor(overload, operation);
}
/// <summary>
/// Registers generated Bind methods as interceptors. This is done differently from other root
/// methods <see cref="RegisterAsInterceptor(Enum, IInvocationOperation)"/> because we need to
/// explicitly account for the type to bind, to avoid type-check issues for polymorphic objects.
/// </summary>
private void RegisterAsInterceptor_ConfigBinder_BindMethod(MethodsToGen_ConfigurationBinder overload, TypeSpec typeSpec, IInvocationOperation operation)
{
_sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload;
_sourceGenSpec.InterceptionInfo_ConfigBinder.RegisterOverloadInfo(overload, typeSpec, operation);
}
}
}
}

View file

@ -34,9 +34,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
// We are going to emit calls to APIs on IServiceCollection.
_sourceGenSpec.TypeNamespaces.Add("Microsoft.Extensions.DependencyInjection");
if (targetMethod.Name is "Bind")
{
RegisterBindInvocation(invocation, typeSpec);
@ -61,21 +58,20 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
if (paramCount is 2)
MethodsToGen_Extensions_OptionsBuilder overload = paramCount switch
{
_sourceGenSpec.MethodsToGen_OptionsBuilderExt |= MethodsToGen_Extensions_OptionsBuilder.Bind_T;
}
else if (paramCount is 3 && SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type))
{
_sourceGenSpec.MethodsToGen_OptionsBuilderExt |= MethodsToGen_Extensions_OptionsBuilder.Bind_T_BinderOptions;
}
else
{
return;
}
2 => MethodsToGen_Extensions_OptionsBuilder.Bind_T,
3 when SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type) =>
MethodsToGen_Extensions_OptionsBuilder.Bind_T_BinderOptions,
_ => MethodsToGen_Extensions_OptionsBuilder.None
};
if (overload is not MethodsToGen_Extensions_OptionsBuilder.None)
{
RegisterAsInterceptor_OptionsBuilder(overload, operation);
RegisterTypeForMethodGen(MethodsToGen_Extensions_ServiceCollection.Configure_T_name_BinderOptions, typeSpec);
}
}
private void ParseBindConfigurationInvocation(BinderInvocation invocation, TypeSpec typeSpec)
{
@ -85,11 +81,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
int paramCount = @params.Length;
Debug.Assert(paramCount >= 2);
if (paramCount is 3 && @params[1].Type.SpecialType is SpecialType.System_String && SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type))
if (paramCount is 3 &&
@params[1].Type.SpecialType is SpecialType.System_String &&
SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type))
{
_sourceGenSpec.MethodsToGen_OptionsBuilderExt |= MethodsToGen_Extensions_OptionsBuilder.BindConfiguration_T_path_BinderOptions;
RegisterTypeForBindCoreUntypedGen(typeSpec);
}
RegisterAsInterceptor_OptionsBuilder(MethodsToGen_Extensions_OptionsBuilder.BindConfiguration_T_path_BinderOptions, invocation.Operation);
RegisterTypeForBindCoreMainGen(typeSpec);
}
}
private void RegisterAsInterceptor_OptionsBuilder(MethodsToGen_Extensions_OptionsBuilder overload, IInvocationOperation operation)
{
_sourceGenSpec.MethodsToGen_OptionsBuilderExt |= overload;
RegisterAsInterceptor(overload, operation);
// Emitting refs to IOptionsChangeTokenSource, ConfigurationChangeTokenSource.
_sourceGenSpec.Namespaces.Add("Microsoft.Extensions.Options");
// Emitting refs to OptionsBuilder<T>.
_sourceGenSpec.Namespaces.Add("Microsoft.Extensions.DependencyInjection");
}
}
}

View file

@ -79,12 +79,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
RegisterTypeForMethodGen(overload, typeSpec);
RegisterAsInterceptor(overload, operation);
}
private void RegisterTypeForMethodGen(MethodsToGen_Extensions_ServiceCollection overload, TypeSpec typeSpec)
{
_sourceGenSpec.MethodsToGen_ServiceCollectionExt |= overload;
RegisterTypeForBindCoreUntypedGen(typeSpec);
_sourceGenSpec.Namespaces.Add("Microsoft.Extensions.DependencyInjection");
RegisterTypeForBindCoreMainGen(typeSpec);
}
}
}

View file

@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisVersion_LatestVS)" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.7.0-3.23314.3" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
@ -26,12 +26,14 @@
<Compile Include="ConfigurationBindingGenerator.cs" />
<Compile Include="ConfigurationBindingGenerator.Emitter.cs" />
<Compile Include="ConfigurationBindingGenerator.Parser.cs" />
<Compile Include="ConfigurationBindingGenerator.Suppressor.cs" />
<Compile Include="Helpers\Emitter\ConfigurationBinder.cs" />
<Compile Include="Helpers\Emitter\CoreBindingHelper.cs" />
<Compile Include="Helpers\Emitter\CoreBindingHelpers.cs" />
<Compile Include="Helpers\Emitter\ExceptionMessages.cs" />
<Compile Include="Helpers\Emitter\Helpers.cs" />
<Compile Include="Helpers\Emitter\OptionsBuilderConfigurationExtensions.cs" />
<Compile Include="Helpers\Emitter\OptionsConfigurationServiceCollectionExtensions.cs" />
<Compile Include="Helpers\InterceptorLocationInfo.cs" />
<Compile Include="Helpers\MethodsToGen.cs" />
<Compile Include="Helpers\Parser\BinderInvocation.cs" />
<Compile Include="Helpers\Parser\ConfigurationBinder.cs" />

View file

@ -22,10 +22,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public List<ParameterSpec> ConstructorParameters { get; } = new();
private string _displayStringWithoutSpecialCharacters;
public string DisplayStringWithoutSpecialCharacters =>
_displayStringWithoutSpecialCharacters ??= $"{MinimalDisplayString.Replace(".", string.Empty).Replace("<", string.Empty).Replace(">", string.Empty)}";
public override bool NeedsMemberBinding => CanInitialize &&
Properties.Values.Count > 0 &&
Properties.Values.Any(p => p.ShouldBind());

View file

@ -24,7 +24,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
_parseMethodName ??= StringParsableTypeKind is StringParsableTypeKind.ByteArray
? "ParseByteArray"
// MinimalDisplayString.Length is certainly > 2.
: $"Parse{(char.ToUpper(MinimalDisplayString[0]) + MinimalDisplayString.Substring(1)).Replace(".", "")}";
: $"Parse{(char.ToUpper(DisplayString[0]) + DisplayString.Substring(1)).Replace(".", "")}";
return _parseMethodName;
}

View file

@ -1,21 +1,25 @@
// 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;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal sealed record SourceGenerationSpec
{
public Dictionary<Enum, List<InterceptorLocationInfo>> InterceptionInfo { get; } = new();
public ConfigurationBinderInterceptorInfo InterceptionInfo_ConfigBinder { get; } = new();
public Dictionary<MethodsToGen_CoreBindingHelper, HashSet<TypeSpec>> TypesForGen_CoreBindingHelper_Methods { get; } = new();
public Dictionary<MethodsToGen_ConfigurationBinder, HashSet<TypeSpec>> TypesForGen_ConfigurationBinder_BindMethods { get; } = new();
public HashSet<ParsableFromStringSpec> PrimitivesForHelperGen { get; } = new();
public HashSet<string> TypeNamespaces { get; } = new()
public HashSet<string> Namespaces { get; } = new()
{
"System",
"System.CodeDom.Compiler",
"System.Globalization",
"System.Runtime.CompilerServices",
"Microsoft.Extensions.Configuration",
};

View file

@ -17,17 +17,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
FullyQualifiedDisplayString = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
MinimalDisplayString = type.ToDisplayString(s_minimalDisplayFormat);
Name = Namespace + "." + MinimalDisplayString.Replace(".", "+");
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
}
public string Name { get; }
public string FullyQualifiedDisplayString { get; }
public string MinimalDisplayString { get; }
public string DisplayString { get; }
public string? Namespace { get; }

View file

@ -133,10 +133,10 @@
<value>The collection element type is not supported: '{0}'.</value>
</data>
<data name="Language VersionIsNotSupportedMessageFormat" xml:space="preserve">
<value>The project's language version has to be at least 'C# 11'.</value>
<value>The project's language version has to be at least 'C# 12'.</value>
</data>
<data name="LanguageVersionIsNotSupportedTitle" xml:space="preserve">
<value>Language version is required to be at least C# 11</value>
<value>Language version is required to be at least C# 12</value>
</data>
<data name="MissingPublicInstanceConstructor" xml:space="preserve">
<value>Cannot create instance of type '{0}' because it is missing a public instance constructor.</value>

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">Jazyková verze projektu musí být alespoň C# 11</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">Jazyková verze projektu musí být alespoň C# 11</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">Verze jazyka musí být alespoň C# 11</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">Verze jazyka musí být alespoň C# 11</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">Die Sprachversion des Projekts muss mindestens „C# 11“ sein</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">Die Sprachversion des Projekts muss mindestens „C# 11“ sein</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">Die Sprachversion muss mindestens C# 11 sein</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">Die Sprachversion muss mindestens C# 11 sein</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">La versión del lenguaje del proyecto debe ser al menos "C# 11".</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">La versión del lenguaje del proyecto debe ser al menos "C# 11".</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">La versión del lenguaje debe ser al menos C# 11</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">La versión del lenguaje debe ser al menos C# 11</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">La version de langage du projet doit être au moins « C# 11 ».</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">La version de langage du projet doit être au moins « C# 11 ».</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">La version du langage doit être au moins C# 11</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">La version du langage doit être au moins C# 11</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">La versione del linguaggio del progetto deve essere almeno 'C# 11'.</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">La versione del linguaggio del progetto deve essere almeno 'C# 11'.</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">La versione del linguaggio deve essere almeno C# 11</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">La versione del linguaggio deve essere almeno C# 11</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">プロジェクトの言語バージョンは少なくとも 'C# 11' である必要があります。</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">プロジェクトの言語バージョンは少なくとも 'C# 11' である必要があります。</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">言語バージョンは少なくとも C# 11 である必要があります</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">言語バージョンは少なくとも C# 11 である必要があります</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">프로젝트의 언어 버전은 'C# 11' 이상이어야 합니다.</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">프로젝트의 언어 버전은 'C# 11' 이상이어야 합니다.</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">언어 버전은 C# 11 이상이어야 합니다.</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">언어 버전은 C# 11 이상이어야 합니다.</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">Wersja językowa projektu musi mieć wartość co najmniej „C# 11”.</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">Wersja językowa projektu musi mieć wartość co najmniej „C# 11”.</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">Wymagana jest wersja językowa co najmniej C# 11</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">Wymagana jest wersja językowa co najmniej C# 11</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">A versão do idioma do projeto deve ser no mínimo 'C# 11'.</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">A versão do idioma do projeto deve ser no mínimo 'C# 11'.</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">A versão do idioma deve ser pelo menos C# 11</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">A versão do idioma deve ser pelo menos C# 11</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">Версия языка проекта должна быть не ниже "C# 11".</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">Версия языка проекта должна быть не ниже "C# 11".</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">Версия языка должна быть не ниже C# 11</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">Версия языка должна быть не ниже C# 11</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">Projenin dil sürümü en az 'C# 11' olmalıdır.</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">Projenin dil sürümü en az 'C# 11' olmalıdır.</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">Dil sürümünün en az C# 11 olması gerekir</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">Dil sürümünün en az C# 11 olması gerekir</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">项目的语言版本必须至少为 "C# 11"。</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">项目的语言版本必须至少为 "C# 11"。</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">语言版本必须至少为 C# 11</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">语言版本必须至少为 C# 11</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -28,13 +28,13 @@
<note />
</trans-unit>
<trans-unit id="Language VersionIsNotSupportedMessageFormat">
<source>The project's language version has to be at least 'C# 11'.</source>
<target state="translated">專案的語言版本必須至少為 'C# 11'。</target>
<source>The project's language version has to be at least 'C# 12'.</source>
<target state="needs-review-translation">專案的語言版本必須至少為 'C# 11'。</target>
<note />
</trans-unit>
<trans-unit id="LanguageVersionIsNotSupportedTitle">
<source>Language version is required to be at least C# 11</source>
<target state="translated">語言版本要求至少為 C#11</target>
<source>Language version is required to be at least C# 12</source>
<target state="needs-review-translation">語言版本要求至少為 C#11</target>
<note />
</trans-unit>
<trans-unit id="MissingPublicInstanceConstructor">

View file

@ -8,7 +8,6 @@ using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
#if BUILDING_SOURCE_GENERATOR_TESTS
using Microsoft.Extensions.Configuration;
#endif
@ -2037,6 +2036,7 @@ if (!System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Launc
ValidateGeolocation(obj);
}
#if !BUILDING_SOURCE_GENERATOR_TESTS
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
public void TraceSwitchTest()
{
@ -2056,6 +2056,7 @@ if (!System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Launc
Assert.Equal("Info", ts.Value);
#endif // NETCOREAPP
}
#endif
private void ValidateGeolocation(IGeolocation location)
{

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions: null) ?? default(T));
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -18,11 +25,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 12, 17)]
public static T? Get<T>(this IConfiguration configuration) => (T?)(GetCore(configuration, typeof(T), configureOptions: null) ?? default(T));
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClassWithCustomCollections = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "CustomDictionary", "CustomList", "IReadOnlyList", "IReadOnlyDictionary" });
public static object? GetCore(this IConfiguration configuration, Type type, Action<BinderOptions>? configureOptions)
@ -46,16 +60,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return obj;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref Program.CustomDictionary<string, int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -67,11 +76,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.CustomList obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -83,11 +87,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -99,11 +98,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref ICollection<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -115,11 +109,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref IReadOnlyList<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (obj is not ICollection<int> temp)
{
return;
@ -136,11 +125,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -152,11 +136,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref IDictionary<string, int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -168,11 +147,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref IReadOnlyDictionary<string, int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (obj is not IDictionary<string, int> temp)
{
return;
@ -189,11 +163,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClassWithCustomCollections obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClassWithCustomCollections), s_configKeys_ProgramMyClassWithCustomCollections, configuration, binderOptions);
if (AsConfigWithChildren(configuration.GetSection("CustomDictionary")) is IConfigurationSection section1)
@ -229,6 +198,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -281,7 +251,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -298,5 +268,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,18 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration, ref obj, binderOptions: null);
using System;
using System.CodeDom.Compiler;
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
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.CoreBindingHelper.BindCore(configuration, ref obj, global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetBinderOptions(configureOptions));
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration.GetSection(key), ref obj, binderOptions: null);
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -23,20 +24,72 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
#region IConfiguration extensions.
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 13, 18)]
public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var typedObj = (Program.MyClass)obj;
BindCore(configuration, ref typedObj, binderOptions: null);
}
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 14, 24)]
public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var typedObj = (Program.MyClass)obj;
BindCore(configuration, ref typedObj, GetBinderOptions(configureOptions));
}
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 15, 24)]
public static void Bind_ProgramMyClass(this IConfiguration configuration, string key, object? obj)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var typedObj = (Program.MyClass)obj;
BindCore(configuration.GetSection(key), ref typedObj, binderOptions: null);
}
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -48,11 +101,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -64,11 +112,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, Program.MyClass2> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null))
@ -81,11 +124,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -120,6 +158,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -163,7 +202,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -180,5 +219,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration, ref obj, binderOptions: null);
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -17,20 +24,36 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
#region IConfiguration extensions.
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 12, 20)]
public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var typedObj = (Program.MyClass)obj;
BindCore(configuration, ref typedObj, binderOptions: null);
}
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -42,11 +65,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -58,11 +76,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, Program.MyClass2> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null))
@ -75,11 +88,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -114,6 +122,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -156,5 +165,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
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.CoreBindingHelper.BindCore(configuration, ref obj, global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetBinderOptions(configureOptions));
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -17,20 +24,36 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
#region IConfiguration extensions.
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 12, 20)]
public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var typedObj = (Program.MyClass)obj;
BindCore(configuration, ref typedObj, GetBinderOptions(configureOptions));
}
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -42,11 +65,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -58,11 +76,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, Program.MyClass2> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null))
@ -75,11 +88,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -114,6 +122,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -157,7 +166,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -174,5 +183,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration.GetSection(key), ref obj, binderOptions: null);
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -17,20 +24,36 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
#region IConfiguration extensions.
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 12, 20)]
public static void Bind_ProgramMyClass(this IConfiguration configuration, string key, object? obj)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var typedObj = (Program.MyClass)obj;
BindCore(configuration.GetSection(key), ref typedObj, binderOptions: null);
}
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" });
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -42,11 +65,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -58,11 +76,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, Program.MyClass2> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null))
@ -75,11 +88,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -114,6 +122,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -156,5 +165,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,21 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions: null) ?? default(T));
using System;
using System.CodeDom.Compiler;
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
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.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions) ?? default(T));
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, type, configureOptions: null);
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
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.CoreBindingHelper.GetCore(configuration, type, configureOptions);
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -26,11 +24,30 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 12, 38)]
public static T? Get<T>(this IConfiguration configuration) => (T?)(GetCore(configuration, typeof(T), configureOptions: null) ?? default(T));
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 14, 36)]
public static T? Get<T>(this IConfiguration configuration, Action<BinderOptions>? configureOptions) => (T?)(GetCore(configuration, typeof(T), configureOptions) ?? default(T));
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 13, 36)]
public static object? Get(this IConfiguration configuration, Type type) => GetCore(configuration, type, configureOptions: null);
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 15, 36)]
public static object? Get(this IConfiguration configuration, Type type, Action<BinderOptions>? configureOptions) => GetCore(configuration, type, configureOptions);
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyArray", "MyDictionary" });
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass2 = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyInt" });
@ -54,24 +71,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
BindCore(configuration, ref obj, binderOptions);
return obj;
}
if (type == typeof(Program.MyClass2))
else if (type == typeof(Program.MyClass2))
{
var obj = new Program.MyClass2();
BindCore(configuration, ref obj, binderOptions);
return obj;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -83,11 +94,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref int[] obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var temp2 = new List<int>();
BindCore(configuration, ref temp2, binderOptions);
int originalCount = obj.Length;
@ -97,11 +103,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -113,11 +114,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -154,11 +150,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
if (configuration["MyInt"] is string value15)
@ -167,6 +158,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -219,7 +211,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -236,5 +228,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,21 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
public static T? GetValue<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, typeof(T), key) ?? default(T));
using System;
using System.CodeDom.Compiler;
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
public static T? GetValue<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, T defaultValue) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, typeof(T), key) ?? defaultValue);
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
public static object? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, string key) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, type, key);
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
public static object? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, string key, object? defaultValue) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, type, key) ?? defaultValue;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -25,11 +23,30 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System;
using System.CodeDom.Compiler;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 13, 18)]
public static T? GetValue<T>(this IConfiguration configuration, string key) => (T?)(BindingExtensions.GetValueCore(configuration, typeof(T), key) ?? default(T));
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 16, 24)]
public static T? GetValue<T>(this IConfiguration configuration, string key, T defaultValue) => (T?)(BindingExtensions.GetValueCore(configuration, typeof(T), key) ?? defaultValue);
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 14, 24)]
public static object? GetValue(this IConfiguration configuration, Type type, string key) => BindingExtensions.GetValueCore(configuration, type, key);
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 17, 24)]
public static object? GetValue(this IConfiguration configuration, Type type, string key, object? defaultValue) => BindingExtensions.GetValueCore(configuration, type, key) ?? defaultValue;
#endregion IConfiguration extensions.
#region Core binding extensions.
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
if (configuration is null)
@ -48,18 +65,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
return ParseInt(value, () => section.Path);
}
if (type == typeof(bool?))
else if (type == typeof(bool?))
{
return ParseBool(value, () => section.Path);
}
if (type == typeof(byte[]))
else if (type == typeof(byte[]))
{
return ParseByteArray(value, () => section.Path);
}
if (type == typeof(CultureInfo))
else if (type == typeof(CultureInfo))
{
return ParseCultureInfo(value, () => section.Path);
}
@ -114,5 +128,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(CultureInfo)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
public static T? GetValue<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, typeof(T), key) ?? default(T));
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -16,11 +23,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System;
using System.CodeDom.Compiler;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 10, 20)]
public static T? GetValue<T>(this IConfiguration configuration, string key) => (T?)(BindingExtensions.GetValueCore(configuration, typeof(T), key) ?? default(T));
#endregion IConfiguration extensions.
#region Core binding extensions.
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
if (configuration is null)
@ -54,5 +68,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
public static T? GetValue<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, T defaultValue) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, typeof(T), key) ?? defaultValue);
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -16,11 +23,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System;
using System.CodeDom.Compiler;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 12, 20)]
public static T? GetValue<T>(this IConfiguration configuration, string key, T defaultValue) => (T?)(BindingExtensions.GetValueCore(configuration, typeof(T), key) ?? defaultValue);
#endregion IConfiguration extensions.
#region Core binding extensions.
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
if (configuration is null)
@ -54,5 +68,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
public static object? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, string key) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, type, key);
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -16,11 +23,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System;
using System.CodeDom.Compiler;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 10, 20)]
public static object? GetValue(this IConfiguration configuration, Type type, string key) => BindingExtensions.GetValueCore(configuration, type, key);
#endregion IConfiguration extensions.
#region Core binding extensions.
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
if (configuration is null)
@ -54,5 +68,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(bool)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
public static object? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, string key, object? defaultValue) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, type, key) ?? defaultValue;
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -16,11 +23,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System;
using System.CodeDom.Compiler;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Extracts the value with the specified key and converts it to the specified type.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 11, 20)]
public static object? GetValue(this IConfiguration configuration, Type type, string key, object? defaultValue) => BindingExtensions.GetValueCore(configuration, type, key) ?? defaultValue;
#endregion IConfiguration extensions.
#region Core binding extensions.
public static object? GetValueCore(this IConfiguration configuration, Type type, string key)
{
if (configuration is null)
@ -54,5 +68,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(CultureInfo)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions: null) ?? default(T));
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -17,11 +24,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 11, 40)]
public static T? Get<T>(this IConfiguration configuration) => (T?)(GetCore(configuration, typeof(T), configureOptions: null) ?? default(T));
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyArray", "MyDictionary" });
public static object? GetCore(this IConfiguration configuration, Type type, Action<BinderOptions>? configureOptions)
@ -45,16 +59,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return obj;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -66,11 +75,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref int[] obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var temp1 = new List<int>();
BindCore(configuration, ref temp1, binderOptions);
int originalCount = obj.Length;
@ -80,11 +84,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -96,11 +95,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -135,6 +129,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -187,7 +182,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -204,5 +199,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
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.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions) ?? default(T));
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -17,11 +24,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 11, 40)]
public static T? Get<T>(this IConfiguration configuration, Action<BinderOptions>? configureOptions) => (T?)(GetCore(configuration, typeof(T), configureOptions) ?? default(T));
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyArray", "MyDictionary" });
public static object? GetCore(this IConfiguration configuration, Type type, Action<BinderOptions>? configureOptions)
@ -45,16 +59,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return obj;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -66,11 +75,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref int[] obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var temp1 = new List<int>();
BindCore(configuration, ref temp1, binderOptions);
int originalCount = obj.Length;
@ -80,11 +84,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -96,11 +95,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -135,6 +129,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -187,7 +182,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -204,5 +199,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, type, configureOptions: null);
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -17,11 +24,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 11, 40)]
public static object? Get(this IConfiguration configuration, Type type) => GetCore(configuration, type, configureOptions: null);
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass2 = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyInt" });
public static object? GetCore(this IConfiguration configuration, Type type, Action<BinderOptions>? configureOptions)
@ -45,16 +59,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return obj;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
if (configuration["MyInt"] is string value1)
@ -63,6 +72,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -115,7 +125,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -132,5 +142,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
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.CoreBindingHelper.GetCore(configuration, type, configureOptions);
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -17,11 +24,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Attempts to bind the configuration instance to a new instance of type T.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 11, 20)]
public static object? Get(this IConfiguration configuration, Type type, Action<BinderOptions>? configureOptions) => GetCore(configuration, type, configureOptions);
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass2 = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyInt" });
public static object? GetCore(this IConfiguration configuration, Type type, Action<BinderOptions>? configureOptions)
@ -45,16 +59,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return obj;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
if (configuration["MyInt"] is string value1)
@ -63,6 +72,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -115,7 +125,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -132,5 +142,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,31 +2,18 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedOptionsBuilderBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Registers the dependency injection container to bind <typeparamref name="TOptions"/> against the <see cref="global::Microsoft.Extensions.Configuration.IConfiguration"/> obtained from the DI service provider.</summary>
public static global::Microsoft.Extensions.Options.OptionsBuilder<TOptions> BindConfiguration<TOptions>(this global::Microsoft.Extensions.Options.OptionsBuilder<TOptions> optionsBuilder, string configSectionPath, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions = null) where TOptions : class
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
if (optionsBuilder is null)
public InterceptsLocationAttribute(string filePath, int line, int column)
{
throw new global::System.ArgumentNullException(nameof(optionsBuilder));
}
if (configSectionPath is null)
{
throw new global::System.ArgumentNullException(nameof(configSectionPath));
}
optionsBuilder.Configure<global::Microsoft.Extensions.Configuration.IConfiguration>((obj, configuration) =>
{
global::Microsoft.Extensions.Configuration.IConfiguration section = string.Equals(string.Empty, configSectionPath, global::System.StringComparison.OrdinalIgnoreCase) ? configuration : configuration.GetSection(configSectionPath);
global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(section, obj, typeof(TOptions), configureOptions);
});
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IOptionsChangeTokenSource<TOptions>, global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource<TOptions>>(optionsBuilder.Services);
return optionsBuilder;
}
}
@ -34,31 +21,74 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region OptionsBuilder<TOptions> extensions.
/// <summary>Registers the dependency injection container to bind <typeparamref name="TOptions"/> against the <see cref="IConfiguration"/> obtained from the DI service provider.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 12, 24)]
public static OptionsBuilder<TOptions> BindConfiguration<TOptions>(this OptionsBuilder<TOptions> optionsBuilder, string configSectionPath, Action<BinderOptions>? configureOptions = null) where TOptions : class
{
if (optionsBuilder is null)
{
throw new ArgumentNullException(nameof(optionsBuilder));
}
if (configSectionPath is null)
{
throw new ArgumentNullException(nameof(configSectionPath));
}
optionsBuilder.Configure<IConfiguration>((obj, configuration) =>
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
IConfiguration section = string.Equals(string.Empty, configSectionPath, StringComparison.OrdinalIgnoreCase) ? configuration : configuration.GetSection(configSectionPath);
BindCoreMain(section, obj, typeof(TOptions), configureOptions);
});
optionsBuilder.Services.AddSingleton<IOptionsChangeTokenSource<TOptions>, ConfigurationChangeTokenSource<TOptions>>();
return optionsBuilder;
}
#endregion OptionsBuilder<TOptions> extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" });
public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (!HasValueOrChildren(configuration))
{
return;
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (type == typeof(Program.MyClass))
{
var temp = (Program.MyClass)obj;
@ -66,16 +96,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -87,11 +112,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -110,6 +130,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -162,7 +183,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -179,5 +200,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,49 +2,18 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedOptionsBuilderBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Registers a configuration instance which <typeparamref name="TOptions"/> will bind against.</summary>
public static global::Microsoft.Extensions.Options.OptionsBuilder<TOptions> Bind<TOptions>(this global::Microsoft.Extensions.Options.OptionsBuilder<TOptions> optionsBuilder, global::Microsoft.Extensions.Configuration.IConfiguration configuration) where TOptions : class
{
return global::GeneratedOptionsBuilderBinder.Bind(optionsBuilder, configuration, configureOptions: null);
}
using System;
using System.CodeDom.Compiler;
/// <summary>Registers a configuration instance which <typeparamref name="TOptions"/> will bind against.</summary>
public static global::Microsoft.Extensions.Options.OptionsBuilder<TOptions> Bind<TOptions>(this global::Microsoft.Extensions.Options.OptionsBuilder<TOptions> optionsBuilder, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) where TOptions : class
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
if (optionsBuilder is null)
public InterceptsLocationAttribute(string filePath, int line, int column)
{
throw new global::System.ArgumentNullException(nameof(optionsBuilder));
}
global::GeneratedServiceCollectionBinder.Configure<TOptions>(optionsBuilder.Services, optionsBuilder.Name, configuration, configureOptions);
return optionsBuilder;
}
}
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedServiceCollectionBinder
{
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<TOptions>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) where TOptions : class
{
if (services is null)
{
throw new global::System.ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new global::System.ArgumentNullException(nameof(configuration));
}
global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services);
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IOptionsChangeTokenSource<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource<TOptions>(name, configuration));
return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions)));
}
}
@ -52,31 +21,79 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region OptionsBuilder<TOptions> extensions.
/// <summary>Registers a configuration instance which <typeparamref name="TOptions"/> will bind against.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 15, 24)]
public static OptionsBuilder<TOptions> Bind<TOptions>(this OptionsBuilder<TOptions> optionsBuilder, IConfiguration configuration) where TOptions : class
{
return Bind(optionsBuilder, configuration, configureOptions: null);
}
/// <summary>Registers a configuration instance which <typeparamref name="TOptions"/> will bind against.</summary>
public static OptionsBuilder<TOptions> Bind<TOptions>(this OptionsBuilder<TOptions> optionsBuilder, IConfiguration configuration, Action<BinderOptions>? configureOptions) where TOptions : class
{
if (optionsBuilder is null)
{
throw new ArgumentNullException(nameof(optionsBuilder));
}
Configure<TOptions>(optionsBuilder.Services, optionsBuilder.Name, configuration, configureOptions);
return optionsBuilder;
}
#endregion OptionsBuilder<TOptions> extensions.
#region IServiceCollection extensions.
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string? name, IConfiguration configuration, Action<BinderOptions>? configureOptions) where TOptions : class
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
OptionsServiceCollectionExtensions.AddOptions(services);
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, configuration));
return services.AddSingleton<Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(new Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions)));
}
#endregion IServiceCollection extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" });
public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (!HasValueOrChildren(configuration))
{
return;
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (type == typeof(Program.MyClass))
{
var temp = (Program.MyClass)obj;
@ -84,16 +101,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -105,11 +117,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -128,6 +135,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -180,7 +188,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -197,5 +205,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,43 +2,18 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedOptionsBuilderBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Registers a configuration instance which <typeparamref name="TOptions"/> will bind against.</summary>
public static global::Microsoft.Extensions.Options.OptionsBuilder<TOptions> Bind<TOptions>(this global::Microsoft.Extensions.Options.OptionsBuilder<TOptions> optionsBuilder, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) where TOptions : class
{
if (optionsBuilder is null)
{
throw new global::System.ArgumentNullException(nameof(optionsBuilder));
}
using System;
using System.CodeDom.Compiler;
global::GeneratedServiceCollectionBinder.Configure<TOptions>(optionsBuilder.Services, optionsBuilder.Name, configuration, configureOptions);
return optionsBuilder;
}
}
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedServiceCollectionBinder
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<TOptions>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) where TOptions : class
public InterceptsLocationAttribute(string filePath, int line, int column)
{
if (services is null)
{
throw new global::System.ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new global::System.ArgumentNullException(nameof(configuration));
}
global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services);
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IOptionsChangeTokenSource<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource<TOptions>(name, configuration));
return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions)));
}
}
@ -46,31 +21,73 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region OptionsBuilder<TOptions> extensions.
/// <summary>Registers a configuration instance which <typeparamref name="TOptions"/> will bind against.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 15, 24)]
public static OptionsBuilder<TOptions> Bind<TOptions>(this OptionsBuilder<TOptions> optionsBuilder, IConfiguration configuration, Action<BinderOptions>? configureOptions) where TOptions : class
{
if (optionsBuilder is null)
{
throw new ArgumentNullException(nameof(optionsBuilder));
}
Configure<TOptions>(optionsBuilder.Services, optionsBuilder.Name, configuration, configureOptions);
return optionsBuilder;
}
#endregion OptionsBuilder<TOptions> extensions.
#region IServiceCollection extensions.
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string? name, IConfiguration configuration, Action<BinderOptions>? configureOptions) where TOptions : class
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
OptionsServiceCollectionExtensions.AddOptions(services);
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, configuration));
return services.AddSingleton<Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(new Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions)));
}
#endregion IServiceCollection extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" });
public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (!HasValueOrChildren(configuration))
{
return;
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (type == typeof(Program.MyClass))
{
var temp = (Program.MyClass)obj;
@ -78,16 +95,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -99,11 +111,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -122,6 +129,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -174,7 +182,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -191,5 +199,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,12 +2,19 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedConfigurationBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration, ref obj, binderOptions: null);
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
@ -17,20 +24,36 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Prop0", "Prop1", "Prop2", "Prop3", "Prop4", "Prop5", "Prop6", "Prop8", "Prop9", "Prop10", "Prop13", "Prop14", "Prop15", "Prop16", "Prop17", "Prop19", "Prop20", "Prop21", "Prop23", "Prop24", "Prop25", "Prop26", "Prop27", "Prop7", "Prop11", "Prop12", "Prop18", "Prop22" });
#region IConfiguration extensions.
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 13, 16)]
public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
var typedObj = (Program.MyClass)obj;
BindCore(configuration, ref typedObj, binderOptions: null);
}
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Prop0", "Prop1", "Prop2", "Prop3", "Prop4", "Prop5", "Prop6", "Prop8", "Prop9", "Prop10", "Prop13", "Prop14", "Prop15", "Prop16", "Prop17", "Prop19", "Prop20", "Prop21", "Prop23", "Prop24", "Prop25", "Prop26", "Prop27", "Prop7", "Prop11", "Prop12", "Prop18", "Prop22" });
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
if (configuration["Prop0"] is string value0)
@ -168,6 +191,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -517,5 +541,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(byte[])}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,64 +2,84 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedServiceCollectionBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<TOptions>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::Microsoft.Extensions.Configuration.IConfiguration configuration) where TOptions : class
{
return global::GeneratedServiceCollectionBinder.Configure<TOptions>(services, string.Empty, configuration, configureOptions: null);
}
using System;
using System.CodeDom.Compiler;
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<TOptions>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) where TOptions : class
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
if (services is null)
public InterceptsLocationAttribute(string filePath, int line, int column)
{
throw new global::System.ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new global::System.ArgumentNullException(nameof(configuration));
}
global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services);
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IOptionsChangeTokenSource<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource<TOptions>(name, configuration));
return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions)));
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IServiceCollection extensions.
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 14, 18)]
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration configuration) where TOptions : class
{
return Configure<TOptions>(services, string.Empty, configuration, configureOptions: null);
}
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string? name, IConfiguration configuration, Action<BinderOptions>? configureOptions) where TOptions : class
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
OptionsServiceCollectionExtensions.AddOptions(services);
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, configuration));
return services.AddSingleton<Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(new Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions)));
}
#endregion IServiceCollection extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass2 = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyInt" });
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" });
public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (!HasValueOrChildren(configuration))
{
return;
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (type == typeof(Program.MyClass))
{
var temp = (Program.MyClass)obj;
@ -67,16 +87,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -88,11 +103,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
if (configuration["MyInt"] is string value1)
@ -103,11 +113,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref List<Program.MyClass2> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
var value = new Program.MyClass2();
@ -118,11 +123,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -134,11 +134,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -173,6 +168,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -225,7 +221,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -242,5 +238,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,64 +2,84 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedServiceCollectionBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<TOptions>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) where TOptions : class
{
return global::GeneratedServiceCollectionBinder.Configure<TOptions>(services, string.Empty, configuration, configureOptions);
}
using System;
using System.CodeDom.Compiler;
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<TOptions>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) where TOptions : class
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
if (services is null)
public InterceptsLocationAttribute(string filePath, int line, int column)
{
throw new global::System.ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new global::System.ArgumentNullException(nameof(configuration));
}
global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services);
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IOptionsChangeTokenSource<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource<TOptions>(name, configuration));
return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions)));
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IServiceCollection extensions.
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 14, 18)]
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration configuration, Action<BinderOptions>? configureOptions) where TOptions : class
{
return Configure<TOptions>(services, string.Empty, configuration, configureOptions);
}
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string? name, IConfiguration configuration, Action<BinderOptions>? configureOptions) where TOptions : class
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
OptionsServiceCollectionExtensions.AddOptions(services);
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, configuration));
return services.AddSingleton<Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(new Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions)));
}
#endregion IServiceCollection extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass2 = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyInt" });
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" });
public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (!HasValueOrChildren(configuration))
{
return;
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (type == typeof(Program.MyClass))
{
var temp = (Program.MyClass)obj;
@ -67,16 +87,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -88,11 +103,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
if (configuration["MyInt"] is string value1)
@ -103,11 +113,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref List<Program.MyClass2> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
var value = new Program.MyClass2();
@ -118,11 +123,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -134,11 +134,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -173,6 +168,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -225,7 +221,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -242,5 +238,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,64 +2,84 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedServiceCollectionBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<TOptions>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration) where TOptions : class
{
return global::GeneratedServiceCollectionBinder.Configure<TOptions>(services, name, configuration, configureOptions: null);
}
using System;
using System.CodeDom.Compiler;
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<TOptions>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) where TOptions : class
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
if (services is null)
public InterceptsLocationAttribute(string filePath, int line, int column)
{
throw new global::System.ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new global::System.ArgumentNullException(nameof(configuration));
}
global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services);
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IOptionsChangeTokenSource<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource<TOptions>(name, configuration));
return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions)));
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IServiceCollection extensions.
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 14, 18)]
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string? name, IConfiguration configuration) where TOptions : class
{
return Configure<TOptions>(services, name, configuration, configureOptions: null);
}
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string? name, IConfiguration configuration, Action<BinderOptions>? configureOptions) where TOptions : class
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
OptionsServiceCollectionExtensions.AddOptions(services);
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, configuration));
return services.AddSingleton<Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(new Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions)));
}
#endregion IServiceCollection extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass2 = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyInt" });
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" });
public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (!HasValueOrChildren(configuration))
{
return;
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (type == typeof(Program.MyClass))
{
var temp = (Program.MyClass)obj;
@ -67,16 +87,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -88,11 +103,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
if (configuration["MyInt"] is string value1)
@ -103,11 +113,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref List<Program.MyClass2> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
var value = new Program.MyClass2();
@ -118,11 +123,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -134,11 +134,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -173,6 +168,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -225,7 +221,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -242,5 +238,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -2,58 +2,78 @@
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
internal static class GeneratedServiceCollectionBinder
namespace System.Runtime.CompilerServices
{
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<TOptions>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) where TOptions : class
{
if (services is null)
{
throw new global::System.ArgumentNullException(nameof(services));
}
using System;
using System.CodeDom.Compiler;
if (configuration is null)
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(string filePath, int line, int column)
{
throw new global::System.ArgumentNullException(nameof(configuration));
}
global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services);
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IOptionsChangeTokenSource<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource<TOptions>(name, configuration));
return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions)));
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
/// <summary>Provide core binding logic.</summary>
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")]
file static class CoreBindingHelper
file static class BindingExtensions
{
#region IServiceCollection extensions.
/// <summary>Registers a configuration instance which TOptions will bind against.</summary>
[InterceptsLocationAttribute(@"src-0.cs", 14, 18)]
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string? name, IConfiguration configuration, Action<BinderOptions>? configureOptions) where TOptions : class
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
OptionsServiceCollectionExtensions.AddOptions(services);
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, configuration));
return services.AddSingleton<Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(new Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions)));
}
#endregion IServiceCollection extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass2 = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyInt" });
private readonly static Lazy<HashSet<string>> s_configKeys_ProgramMyClass = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" });
public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action<BinderOptions>? configureOptions)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
if (!HasValueOrChildren(configuration))
{
return;
}
BinderOptions? binderOptions = GetBinderOptions(configureOptions);
if (type == typeof(Program.MyClass))
{
var temp = (Program.MyClass)obj;
@ -61,16 +81,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
return;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input.");
}
public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -82,11 +97,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions);
if (configuration["MyInt"] is string value1)
@ -97,11 +107,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref List<Program.MyClass2> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
var value = new Program.MyClass2();
@ -112,11 +117,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
foreach (IConfigurationSection section in configuration.GetChildren())
{
if (section.Value is string value)
@ -128,11 +128,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions);
obj.MyString = configuration["MyString"]!;
@ -167,6 +162,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}
/// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
public static void ValidateConfigurationKeys(Type type, Lazy<HashSet<string>> keys, IConfiguration configuration, BinderOptions? binderOptions)
{
@ -219,7 +215,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
if (binderOptions.BindNonPublicProperties)
{
throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'.");
}
return binderOptions;
@ -236,5 +232,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception);
}
}
#endregion Core binding extensions.
}
}

View file

@ -8,6 +8,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
{
public partial class ConfigurationBindingGeneratorTests
{
#region IServiceCollection extensions.
private string GetConfigureSource(string paramList) => $$"""
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
@ -40,7 +41,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
}
}
""";
#endregion IServiceCollection extensions.
#region OptionsBuilder<T> extensions.
[Fact]
public async Task Configure_T() =>
await VerifyAgainstBaselineUsingFile("Configure_T.generated.txt", GetConfigureSource("section"), extType: ExtensionClassType.ServiceCollection);
@ -126,5 +129,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
await VerifyAgainstBaselineUsingFile("BindConfiguration.generated.txt", GetSource(), extType: ExtensionClassType.OptionsBuilder);
await VerifyAgainstBaselineUsingFile("BindConfiguration.generated.txt", GetSource(@", _ => { }"), extType: ExtensionClassType.OptionsBuilder);
}
#endregion OptionsBuilder<T> extensions.
}
}

View file

@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
@ -44,7 +43,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
[Theory]
[InlineData(LanguageVersion.Preview)]
[InlineData(LanguageVersion.CSharp11)]
public async Task Bind(LanguageVersion langVersion) =>
await VerifyAgainstBaselineUsingFile("Bind.generated.txt", BindCallSampleCode, langVersion, extType: ExtensionClassType.ConfigurationBinder);

View file

@ -52,15 +52,17 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
ServiceCollection,
}
[Fact]
public async Task LangVersionMustBeCharp11OrHigher()
[Theory]
[InlineData(LanguageVersion.CSharp11)]
[InlineData(LanguageVersion.CSharp10)]
public async Task LangVersionMustBeCharp12OrHigher(LanguageVersion langVersion)
{
var (d, r) = await RunGenerator(BindCallSampleCode, LanguageVersion.CSharp10);
var (d, r) = await RunGenerator(BindCallSampleCode, langVersion);
Assert.Empty(r);
Diagnostic diagnostic = Assert.Single(d);
Assert.True(diagnostic.Id == "SYSLIB1102");
Assert.Contains("C# 11", diagnostic.Descriptor.Title.ToString(CultureInfo.InvariantCulture));
Assert.Contains("C# 12", diagnostic.Descriptor.Title.ToString(CultureInfo.InvariantCulture));
Assert.Equal(DiagnosticSeverity.Error, diagnostic.Severity);
}
@ -250,9 +252,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
Assert.Single(r);
string generatedSource = string.Join('\n', r[0].SourceText.Lines.Select(x => x.ToString()));
Assert.Contains($"public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass0 obj) => {{ }};", generatedSource);
Assert.Contains($"public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass1 obj, global::System.Action<global::Microsoft.Extensions.Configuration.BinderOptions>? configureOptions) => {{ }};", generatedSource);
Assert.Contains($"public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, global::Program.MyClass2 obj) => {{ }};", generatedSource);
Assert.Contains("public static void Bind_ProgramMyClass0(this IConfiguration configuration, object? obj)", generatedSource);
Assert.Contains("public static void Bind_ProgramMyClass1(this IConfiguration configuration, object? obj, Action<BinderOptions>? configureOptions)", generatedSource);
Assert.Contains("public static void Bind_ProgramMyClass2(this IConfiguration configuration, string key, object? obj)", generatedSource);
Assert.Empty(d);
}
@ -395,7 +397,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
private static async Task<(ImmutableArray<Diagnostic>, ImmutableArray<GeneratedSourceResult>)> RunGenerator(
string testSourceCode,
LanguageVersion langVersion = LanguageVersion.CSharp11,
LanguageVersion langVersion = LanguageVersion.Preview,
IEnumerable<Assembly>? references = null) =>
await RoslynTestUtils.RunGenerator(
new ConfigurationBindingGenerator(),

View file

@ -4,7 +4,7 @@
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<!-- Type not supported; property on type not suppported -->
<NoWarn>SYSLIB1100,SYSLIB1101</NoWarn>
<!-- The SDK disables the configuration binding generator by default no matter how it was referenced, so we need to enable it here for testing. -->
<Features>$(Features);InterceptorsPreview</Features>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>

View file

@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
<EnableDefaultItems>true</EnableDefaultItems>
<Features>$(Features);InterceptorsPreview</Features>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
<IsPackable>true</IsPackable>
<PackageDescription>Configuration support for Microsoft.Extensions.Logging.</PackageDescription>

View file

@ -7,7 +7,9 @@
<DefineConstants>$(DefineConstants);NO_SUPPRESS_GC_TRANSITION</DefineConstants>
<IncludePlatformAttributes>true</IncludePlatformAttributes>
<IsPackable>true</IsPackable>
<Features>$(Features);InterceptorsPreview</Features>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<PackageDescription>Console logger provider implementation for Microsoft.Extensions.Logging.</PackageDescription>
</PropertyGroup>

View file

@ -2,9 +2,9 @@
<PropertyGroup>
<Nullable>enable</Nullable>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetFrameworkMinimum)</TargetFrameworks>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<DefineConstants>$(DefineConstants);BUILDING_SOURCE_GENERATOR_TESTS;ROSLYN4_0_OR_GREATER;ROSLYN4_4_OR_GREATER</DefineConstants>
<!-- The SDK disables the configuration binding generator by default no matter how it was referenced, so we need to enable it here for testing. -->
<Features>$(Features);InterceptorsPreview</Features>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>