|
|
13 часов назад | |
|---|---|---|
| .. | ||
| Apq.Cfg.SourceGenerator.csproj | 13 часов назад | |
| CfgSectionAttribute.cs | 1 день назад | |
| CfgSectionGenerator.cs | 1 день назад | |
| CodeEmitter.cs | 1 день назад | |
| Directory.Build.props | 1 день назад | |
| IsExternalInit.cs | 1 день назад | |
| README.md | 1 день назад | |
基于 Roslyn 的源生成器,为 Apq.Cfg 配置类自动生成零反射的绑定代码,支持 Native AOT。
dotnet add package Apq.Cfg.SourceGenerator
使用 [CfgSection] 特性标记配置类,类必须是 partial 的:
using Apq.Cfg;
[CfgSection("AppSettings")]
public partial class AppConfig
{
public string? Name { get; set; }
public int Port { get; set; }
public DatabaseConfig? Database { get; set; }
}
[CfgSection]
public partial class DatabaseConfig
{
public string? ConnectionString { get; set; }
public int Timeout { get; set; } = 30;
}
源生成器会自动为每个配置类生成 BindFrom 和 BindTo 静态方法:
// 从配置节创建新实例
var config = AppConfig.BindFrom(cfgRoot.GetSection("AppSettings"));
// 或绑定到已有实例
var existingConfig = new AppConfig();
AppConfig.BindTo(cfgRoot.GetSection("AppSettings"), existingConfig);
如果在 [CfgSection] 中指定了 SectionPath,还会生成 ICfgRoot 扩展方法:
// 直接从 ICfgRoot 获取配置
var config = cfgRoot.GetAppConfig();
string, int, long, short, byte, sbyteuint, ulong, ushortfloat, double, decimalbool, charDateTime, DateTimeOffset, TimeSpanGuid, UriDateOnly, TimeOnly (.NET 6+)T[] (数组)List<T>HashSet<T>Dictionary<TKey, TValue>[CfgSection])对于上面的 AppConfig 类,源生成器会生成类似以下的代码:
partial class AppConfig
{
public static AppConfig BindFrom(ICfgSection section)
{
if (section == null) throw new ArgumentNullException(nameof(section));
var result = new AppConfig();
BindTo(section, result);
return result;
}
public static void BindTo(ICfgSection section, AppConfig target)
{
if (section == null) throw new ArgumentNullException(nameof(section));
if (target == null) throw new ArgumentNullException(nameof(target));
// Name: string?
{
var __value = section.Get("Name");
if (__value != null)
{
target.Name = __value;
}
}
// Port: int
{
var __value = section.Get("Port");
if (__value != null)
{
var __converted = int.TryParse(__value, out var __intVal) ? __intVal : (int?)null;
if (__converted != null) target.Port = __converted.Value;
}
}
// Database: DatabaseConfig? (复杂对象)
{
var __childSection = section.GetSection("Database");
var __childKeys = __childSection.GetChildKeys().ToList();
if (__childKeys.Count > 0)
{
target.Database = DatabaseConfig.BindFrom(__childSection);
}
}
}
}
在项目文件中添加以下配置可以保留生成的源代码:
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
生成的文件将位于 obj/GeneratedFiles/ 目录下。
using Apq.Cfg;
// 使用 CfgBuilder 创建配置根
var cfgRoot = CfgBuilder.Create()
.AddJson("config.json")
.AddIni("config.ini")
.AddEnvironmentVariables("APP_")
.Build();
// 使用源生成器绑定配置
var appConfig = AppConfig.BindFrom(cfgRoot.GetSection("App"));
Console.WriteLine($"App: {appConfig.Name}");
Console.WriteLine($"Port: {appConfig.Port}");
Console.WriteLine($"Database: {appConfig.Database?.ConnectionString}");
partial 类)MIT License