Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A .NET 8-tól kezdve egy konfigurációkötési forrásgenerátor lett bevezetve, amely elfogja az adott hívási helyeket, és létrehozza a funkciójukat. Ez a funkció natív idő előtti (AOT) és trimmelésbarát módot biztosít a konfigurációs kötőelem használatához, a tükröződésalapú implementáció nélkül. A tükrözés dinamikus kódlétrehozást igényel, amelyet az AOT-forgatókönyvek nem támogatnak.
Ez a funkció a C# 12-ben bevezetett C# elfogók megjelenésével lehetséges. Az elfogók lehetővé teszik, hogy a fordító olyan forráskódot hozzon létre, amely elfog bizonyos hívásokat, és lefordítja őket generált kóddal.
A konfigurációs forrásgenerátor engedélyezése
A konfigurációs forrásgenerátor engedélyezéséhez adja hozzá a következő tulajdonságot a projektfájlhoz:
<PropertyGroup>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>
Ha a konfigurációs forrásgenerátor engedélyezve van, a fordító létrehoz egy forrásfájlt, amely tartalmazza a konfigurációkötési kódot. A létrehozott forrás elfogja a következő osztályok kötési API-jait:
- Microsoft.Extensions.Configuration.ConfigurationBinder
- Microsoft.Extensions.DependencyInjection.OptionsBuilderConfigurationExtensions
- Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions
Más szóval, minden API, amely végül ezekbe a különböző kötési módszerekbe hív, elfogásra kerül és generált kóddal lesz helyettesítve.
Példa használatra
Fontolja meg a natív AOT-alkalmazásként való közzétételre konfigurált .NET-konzolalkalmazást. Az alábbi kód bemutatja, hogyan használható a konfigurációs forrásgenerátor a konfigurációs beállítások kötésére:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>console_binder_gen</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.1" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.22" />
</ItemGroup>
</Project>
Az előző projektfájl engedélyezi a konfigurációs forrásgenerátort azzal, hogy a EnableConfigurationBindingGenerator tulajdonságot true állapotba állítja.
Ezután vegye figyelembe a Program.cs fájlt:
using Microsoft.Extensions.Configuration;
var builder = new ConfigurationBuilder()
.AddInMemoryCollection(initialData: [
new("port", "5001"),
new("enabled", "true"),
new("apiUrl", "https://jsonplaceholder.typicode.com/")
]);
var configuration = builder.Build();
var settings = new Settings();
configuration.Bind(settings);
// Write the values to the console.
Console.WriteLine($"Port = {settings.Port}");
Console.WriteLine($"Enabled = {settings.Enabled}");
Console.WriteLine($"API URL = {settings.ApiUrl}");
class Settings
{
public int Port { get; set; }
public bool Enabled { get; set; }
public string? ApiUrl { get; set; }
}
// This will output the following:
// Port = 5001
// Enabled = True
// API URL = https://jsonplaceholder.typicode.com/
A fenti kód a következőket végzi el:
- Létrehoz egy konfigurációszerkesztő-példányt.
- Meghívja a AddInMemoryCollection-t, és három konfigurációs forrásértéket határoz meg.
- A konfiguráció összeállítására hívja Build().
- A metódus használatával ConfigurationBinder.Bind köti az
Settingsobjektumot a konfigurációs értékekhez.
Az alkalmazás létrehozásakor a konfigurációs forrásgenerátor elfogja a Bind hívást, és előállítja a közvetítői kódot.
Fontos
Ha a PublishAot tulajdonság értéke true (vagy bármely más AOT-figyelmeztetés engedélyezése esetén) és a EnabledConfigurationBindingGenerator tulajdonság értéke false, akkor figyelmeztetés IL2026 lép fel. Ez a figyelmeztetés azt jelzi, hogy a RequiresUnreferencedCode attribútummal rendelkező tagok megszakadhatnak a vágáskor. További információ: IL2026.
A forrás által létrehozott kód megismerése
Az alábbi kódot a konfigurációs forrásgenerátor hozza létre az előző példához:
// <auto-generated/>
#nullable enable annotations
#nullable disable warnings
// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0612, CS0618
namespace System.Runtime.CompilerServices
{
using System;
using System.CodeDom.Compiler;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "9.0.10.47305")]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : Attribute
{
public InterceptsLocationAttribute(int version, string data)
{
}
}
}
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
using Microsoft.Extensions.Configuration;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
[GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "9.0.10.47305")]
file static class BindingExtensions
{
#region IConfiguration extensions.
/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>
[InterceptsLocation(1, "uDIs2gDFz/yEvxOzjNK4jnIBAABQcm9ncmFtLmNz")] // D:\source\WorkerService1\WorkerService1\Program.cs(13,15)
public static void Bind_Settings(this IConfiguration configuration, object? instance)
{
ArgumentNullException.ThrowIfNull(configuration);
if (instance is null)
{
return;
}
var typedObj = (global::Settings)instance;
BindCore(configuration, ref typedObj, defaultValueIfNotFound: false, binderOptions: null);
}
#endregion IConfiguration extensions.
#region Core binding extensions.
private readonly static Lazy<HashSet<string>> s_configKeys_Settings = new(() => new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Port", "Enabled", "ApiUrl" });
public static void BindCore(IConfiguration configuration, ref global::Settings instance, bool defaultValueIfNotFound, BinderOptions? binderOptions)
{
ValidateConfigurationKeys(typeof(global::Settings), s_configKeys_Settings, configuration, binderOptions);
if (configuration["Port"] is string value0 && !string.IsNullOrEmpty(value0))
{
instance.Port = ParseInt(value0, configuration.GetSection("Port").Path);
}
else if (defaultValueIfNotFound)
{
instance.Port = instance.Port;
}
if (configuration["Enabled"] is string value1 && !string.IsNullOrEmpty(value1))
{
instance.Enabled = ParseBool(value1, configuration.GetSection("Enabled").Path);
}
else if (defaultValueIfNotFound)
{
instance.Enabled = instance.Enabled;
}
if (configuration["ApiUrl"] is string value2)
{
instance.ApiUrl = value2;
}
else if (defaultValueIfNotFound)
{
var currentValue = instance.ApiUrl;
if (currentValue is not null)
{
instance.ApiUrl = currentValue;
}
}
}
/// <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 int ParseInt(string value, string? path)
{
try
{
return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception exception)
{
throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(int)}'.", exception);
}
}
public static bool ParseBool(string value, string? path)
{
try
{
return bool.Parse(value);
}
catch (Exception exception)
{
throw new InvalidOperationException($"Failed to convert configuration value at '{path}' to type '{typeof(bool)}'.", exception);
}
}
#endregion Core binding extensions.
}
}
Feljegyzés
Ez a létrehozott kód a konfigurációs forrásgenerátor verziója alapján változhat.
A létrehozott kód tartalmazza az BindingExtensions osztályt, amely a BindCore tényleges kötést végrehajtó metódust tartalmazza. A Bind_Settings metódus meghívja a BindCore metódust, és a példányt a megadott típusra veti.
A létrehozott kód megtekintéséhez állítsa be a <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> elemet a projektfájlban. Ez biztosítja, hogy a fájlok láthatóak legyenek a fejlesztő számára ellenőrzés céljából. A létrehozott kódot megtekintheti a Visual Studio Megoldáskezelőjában, a projekt Függőségek>Elemzők>Microsoft.Extensions.Configuration.Binder.SourceGeneration csomópontjában.