RSCG – Microsoft.Extensions.Configuration.Binder
RSCG – Microsoft.Extensions.Configuration.Binder
name | Microsoft.Extensions.Configuration.Binder |
nuget | https://www.nuget.org/packages/Microsoft.Extensions.Configuration.Binder/ |
link | https://github.com/dotnet/runtime |
author | Microsoft |
Generating Binding for configuration files
This is how you can use Microsoft.Extensions.Configuration.Binder .
The code that you start with is
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <InvariantGlobalization>true</InvariantGlobalization> </PropertyGroup> <ItemGroup> <!--<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />--> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> </ItemGroup> <PropertyGroup> <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator> </PropertyGroup> <PropertyGroup> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> </PropertyGroup> </Project>
The code that you will use is
using ConfigBinderDemo; using Microsoft.Extensions.Options; var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } builder.Services.AddOptions<MyAppOptions>() .BindConfiguration(MyAppOptions.ConfigName); app.MapGet("/nameApp", (IOptions<MyAppOptions> opt) => { try { var val = opt.Value.AppDisplayName; return val; } catch (OptionsValidationException ex) { var problems = ex.Failures.ToArray(); return string.Join(",", problems); } }) .WithName("GetWeatherForecast") .WithOpenApi(); app.Run(); internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); }
using System.Diagnostics; namespace ConfigBinderDemo; [DebuggerDisplay("{AppDisplayName}")] public class MyAppOptions { public const string ConfigName = "MyAppOptionsInConfig"; public string AppDisplayName { get; set; } = string.Empty; }
The code that is generated is
// <auto-generated/> #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. namespace System.Runtime.CompilerServices { using System; using System.CodeDom.Compiler; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "8.0.9.3103")] [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] file sealed class InterceptsLocationAttribute : Attribute { public InterceptsLocationAttribute(string filePath, int line, int column) { } } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { using ConfigBinderDemo; 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; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "8.0.9.3103")] 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> [InterceptsLocation(@"D:\gth\RSCG_Examples\v2\rscg_examples\ConfigBinder\src\ConfigBinderDemo\Program.cs", 20, 14)] public static OptionsBuilder<TOptions> BindConfiguration<TOptions>(this OptionsBuilder<TOptions> optionsBuilder, string configSectionPath, Action<BinderOptions>? configureBinder = 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>((instance, config) => { if (config is null) { throw new ArgumentNullException(nameof(config)); } IConfiguration section = string.Equals(string.Empty, configSectionPath, StringComparison.OrdinalIgnoreCase) ? config : config.GetSection(configSectionPath); BindCoreMain(section, instance, typeof(TOptions), configureBinder); }); 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_MyAppOptions = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "AppDisplayName" }); public static void BindCoreMain(IConfiguration configuration, object instance, Type type, Action<BinderOptions>? configureOptions) { if (instance is null) { return; } if (!HasValueOrChildren(configuration)) { return; } BinderOptions? binderOptions = GetBinderOptions(configureOptions); if (type == typeof(MyAppOptions)) { var temp = (MyAppOptions)instance; BindCore(configuration, ref temp, defaultValueIfNotFound: false, binderOptions); return; } throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref MyAppOptions instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { ValidateConfigurationKeys(typeof(MyAppOptions), s_configKeys_MyAppOptions, configuration, binderOptions); if (configuration["AppDisplayName"] is string value1) { instance.AppDisplayName = value1; } } /// <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) { if (binderOptions?.ErrorOnUnknownConfiguration is true) { List<string>? temp = null; foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) { (temp ??= new List<string>()).Add($"'{section.Key}'"); } } if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); } } } public static bool HasValueOrChildren(IConfiguration configuration) { if ((configuration as IConfigurationSection)?.Value is not null) { return true; } return AsConfigWithChildren(configuration) is not null; } public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) { foreach (IConfigurationSection _ in configuration.GetChildren()) { return configuration; } return null; } public static BinderOptions? GetBinderOptions(Action<BinderOptions>? configureOptions) { if (configureOptions is null) { return null; } BinderOptions binderOptions = new(); configureOptions(binderOptions); if (binderOptions.BindNonPublicProperties) { throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; } #endregion Core binding extensions. } }
Code and pdf at https://ignatandrei.github.io/RSCG_Examples/v2/docs/Microsoft.Extensions.Configuration.Binder
Leave a Reply