SourceGeneratorDemo.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. using Apq.Cfg;
  2. namespace Apq.Cfg.Samples.Demos;
  3. /// <summary>
  4. /// 示例 17: 源生成器(零反射绑定)
  5. /// 演示如何使用 Apq.Cfg.SourceGenerator 实现零反射配置绑定
  6. /// </summary>
  7. public static class SourceGeneratorDemo
  8. {
  9. public static async Task RunAsync(string baseDir)
  10. {
  11. Console.WriteLine("═══════════════════════════════════════════════════════════════");
  12. Console.WriteLine("示例 17: 源生成器(零反射绑定)");
  13. Console.WriteLine("═══════════════════════════════════════════════════════════════\n");
  14. Console.WriteLine("【源生成器说明】");
  15. Console.WriteLine("Apq.Cfg.SourceGenerator 使用 Roslyn 源生成器在编译时生成配置绑定代码。");
  16. Console.WriteLine("优势:零反射、Native AOT 兼容、编译时类型检查。\n");
  17. // 示例 1: 基本用法
  18. Console.WriteLine("--- 示例 17.1: 基本用法 ---");
  19. ShowBasicUsage();
  20. // 示例 2: 定义配置类
  21. Console.WriteLine("\n--- 示例 17.2: 定义配置类 ---");
  22. ShowConfigClassDefinition();
  23. // 示例 3: 使用生成的绑定方法
  24. Console.WriteLine("\n--- 示例 17.3: 使用生成的绑定方法 ---");
  25. ShowBindingMethods();
  26. // 示例 4: 支持的类型
  27. Console.WriteLine("\n--- 示例 17.4: 支持的类型 ---");
  28. ShowSupportedTypes();
  29. // 示例 5: 嵌套配置
  30. Console.WriteLine("\n--- 示例 17.5: 嵌套配置 ---");
  31. ShowNestedConfiguration();
  32. // 示例 6: 集合类型
  33. Console.WriteLine("\n--- 示例 17.6: 集合类型 ---");
  34. ShowCollectionTypes();
  35. // 示例 7: 查看生成的代码
  36. Console.WriteLine("\n--- 示例 17.7: 查看生成的代码 ---");
  37. ShowGeneratedCode();
  38. // 示例 8: 实际演示
  39. Console.WriteLine("\n--- 示例 17.8: 实际演示 ---");
  40. await DemoActualUsageAsync(baseDir);
  41. Console.WriteLine("\n示例 17 完成\n");
  42. }
  43. private static void ShowBasicUsage()
  44. {
  45. Console.WriteLine("基本用法:");
  46. Console.WriteLine(@"
  47. // 1. 安装 NuGet 包
  48. // dotnet add package Apq.Cfg.SourceGenerator
  49. // 2. 定义配置类(必须是 partial 类)
  50. [CfgSection(""AppSettings"")]
  51. public partial class AppConfig
  52. {
  53. public string? Name { get; set; }
  54. public int Port { get; set; }
  55. }
  56. // 3. 使用生成的绑定方法
  57. var cfg = new CfgBuilder()
  58. .AddJson(""config.json"")
  59. .Build();
  60. var appConfig = AppConfig.BindFrom(cfg.GetSection(""AppSettings""));
  61. Console.WriteLine($""Name: {appConfig.Name}, Port: {appConfig.Port}"");
  62. ");
  63. }
  64. private static void ShowConfigClassDefinition()
  65. {
  66. Console.WriteLine("配置类定义规则:");
  67. Console.WriteLine(@"
  68. // 1. 使用 [CfgSection] 特性标记
  69. // 2. 类必须是 partial 的
  70. // 3. 属性必须有 public getter 和 setter
  71. using Apq.Cfg;
  72. // 指定配置节路径
  73. [CfgSection(""Database"")]
  74. public partial class DatabaseConfig
  75. {
  76. public string? Host { get; set; }
  77. public int Port { get; set; } = 5432; // 支持默认值
  78. public string? Name { get; set; }
  79. }
  80. // 不指定路径时,使用类名(去掉 Config/Settings 后缀)
  81. [CfgSection]
  82. public partial class LoggingConfig
  83. {
  84. public string? Level { get; set; }
  85. public bool EnableConsole { get; set; }
  86. }
  87. // 禁用扩展方法生成
  88. [CfgSection(""Cache"", GenerateExtension = false)]
  89. public partial class CacheConfig
  90. {
  91. public string? Provider { get; set; }
  92. public int ExpirationMinutes { get; set; }
  93. }
  94. ");
  95. }
  96. private static void ShowBindingMethods()
  97. {
  98. Console.WriteLine("生成的绑定方法:");
  99. Console.WriteLine(@"
  100. // 源生成器会为每个配置类生成以下方法:
  101. // 1. BindFrom - 从配置节创建新实例
  102. var config = DatabaseConfig.BindFrom(cfgRoot.GetSection(""Database""));
  103. // 2. BindTo - 绑定到已有实例
  104. var existingConfig = new DatabaseConfig();
  105. DatabaseConfig.BindTo(cfgRoot.GetSection(""Database""), existingConfig);
  106. // 3. 扩展方法(如果指定了 SectionPath)
  107. var config2 = cfgRoot.GetDatabaseConfig(); // 自动生成的扩展方法
  108. ");
  109. }
  110. private static void ShowSupportedTypes()
  111. {
  112. Console.WriteLine("支持的类型:");
  113. Console.WriteLine(@"
  114. [CfgSection(""TypeDemo"")]
  115. public partial class TypeDemoConfig
  116. {
  117. // 字符串
  118. public string? StringValue { get; set; }
  119. // 数值类型
  120. public int IntValue { get; set; }
  121. public long LongValue { get; set; }
  122. public double DoubleValue { get; set; }
  123. public decimal DecimalValue { get; set; }
  124. // 布尔
  125. public bool BoolValue { get; set; }
  126. // 日期时间
  127. public DateTime DateTimeValue { get; set; }
  128. public DateTimeOffset DateTimeOffsetValue { get; set; }
  129. public TimeSpan TimeSpanValue { get; set; }
  130. public DateOnly DateOnlyValue { get; set; } // .NET 6+
  131. public TimeOnly TimeOnlyValue { get; set; } // .NET 6+
  132. // 其他
  133. public Guid GuidValue { get; set; }
  134. public Uri? UriValue { get; set; }
  135. // 枚举
  136. public LogLevel LogLevel { get; set; }
  137. // 可空类型
  138. public int? NullableInt { get; set; }
  139. public DateTime? NullableDateTime { get; set; }
  140. }
  141. public enum LogLevel { Debug, Info, Warning, Error }
  142. ");
  143. }
  144. private static void ShowNestedConfiguration()
  145. {
  146. Console.WriteLine("嵌套配置:");
  147. Console.WriteLine(@"
  148. // 嵌套的配置类也需要标记 [CfgSection]
  149. [CfgSection(""App"")]
  150. public partial class AppConfig
  151. {
  152. public string? Name { get; set; }
  153. public DatabaseConfig? Database { get; set; } // 嵌套对象
  154. public LoggingConfig? Logging { get; set; }
  155. }
  156. [CfgSection]
  157. public partial class DatabaseConfig
  158. {
  159. public string? ConnectionString { get; set; }
  160. public int Timeout { get; set; } = 30;
  161. }
  162. [CfgSection]
  163. public partial class LoggingConfig
  164. {
  165. public string? Level { get; set; }
  166. public bool EnableConsole { get; set; }
  167. }
  168. // 对应的 JSON 配置:
  169. // {
  170. // ""App"": {
  171. // ""Name"": ""MyApp"",
  172. // ""Database"": {
  173. // ""ConnectionString"": ""Server=localhost;..."",
  174. // ""Timeout"": 60
  175. // },
  176. // ""Logging"": {
  177. // ""Level"": ""Info"",
  178. // ""EnableConsole"": true
  179. // }
  180. // }
  181. // }
  182. ");
  183. }
  184. private static void ShowCollectionTypes()
  185. {
  186. Console.WriteLine("集合类型:");
  187. Console.WriteLine(@"
  188. [CfgSection(""Collections"")]
  189. public partial class CollectionsConfig
  190. {
  191. // 数组
  192. public string[]? Tags { get; set; }
  193. public int[]? Ports { get; set; }
  194. // List
  195. public List<string>? Hosts { get; set; }
  196. // HashSet
  197. public HashSet<string>? AllowedOrigins { get; set; }
  198. // Dictionary
  199. public Dictionary<string, string>? Headers { get; set; }
  200. public Dictionary<string, int>? Limits { get; set; }
  201. }
  202. // 对应的 JSON 配置:
  203. // {
  204. // ""Collections"": {
  205. // ""Tags"": [""web"", ""api"", ""v2""],
  206. // ""Ports"": [80, 443, 8080],
  207. // ""Hosts"": [""host1.com"", ""host2.com""],
  208. // ""AllowedOrigins"": [""https://example.com""],
  209. // ""Headers"": {
  210. // ""X-Api-Key"": ""secret"",
  211. // ""X-Version"": ""1.0""
  212. // },
  213. // ""Limits"": {
  214. // ""MaxConnections"": 100,
  215. // ""MaxRequests"": 1000
  216. // }
  217. // }
  218. // }
  219. ");
  220. }
  221. private static void ShowGeneratedCode()
  222. {
  223. Console.WriteLine("查看生成的代码:");
  224. Console.WriteLine(@"
  225. // 在项目文件中添加以下配置可以保留生成的源代码:
  226. <PropertyGroup>
  227. <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  228. <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
  229. </PropertyGroup>
  230. // 生成的文件将位于 obj/GeneratedFiles/ 目录下
  231. // 生成的代码示例:
  232. partial class DatabaseConfig
  233. {
  234. public static DatabaseConfig BindFrom(ICfgSection section)
  235. {
  236. if (section == null) throw new ArgumentNullException(nameof(section));
  237. var result = new DatabaseConfig();
  238. BindTo(section, result);
  239. return result;
  240. }
  241. public static void BindTo(ICfgSection section, DatabaseConfig target)
  242. {
  243. if (section == null) throw new ArgumentNullException(nameof(section));
  244. if (target == null) throw new ArgumentNullException(nameof(target));
  245. // ConnectionString: string?
  246. {
  247. var __value = section.Get(""ConnectionString"");
  248. if (__value != null)
  249. {
  250. target.ConnectionString = __value;
  251. }
  252. }
  253. // Timeout: int
  254. {
  255. var __value = section.Get(""Timeout"");
  256. if (__value != null)
  257. {
  258. var __converted = int.TryParse(__value, out var __intVal) ? __intVal : (int?)null;
  259. if (__converted != null) target.Timeout = __converted.Value;
  260. }
  261. }
  262. }
  263. }
  264. ");
  265. }
  266. private static async Task DemoActualUsageAsync(string baseDir)
  267. {
  268. Console.WriteLine("实际演示(使用内存配置):");
  269. // 创建一个简单的配置
  270. var cfg = new CfgBuilder()
  271. .AddJson(Path.Combine(baseDir, "config.json"), level: 0, writeable: false, optional: true, reloadOnChange: false)
  272. .Build();
  273. // 由于源生成器需要在编译时生成代码,这里只能展示概念
  274. Console.WriteLine(@"
  275. // 假设我们有以下配置类:
  276. // [CfgSection(""Database"")]
  277. // public partial class DatabaseConfig
  278. // {
  279. // public string? Host { get; set; }
  280. // public int Port { get; set; }
  281. // public string? Name { get; set; }
  282. // }
  283. // 使用方式:
  284. // var dbConfig = DatabaseConfig.BindFrom(cfg.GetSection(""Database""));
  285. // Console.WriteLine($""Host: {dbConfig.Host}"");
  286. // Console.WriteLine($""Port: {dbConfig.Port}"");
  287. // Console.WriteLine($""Name: {dbConfig.Name}"");
  288. ");
  289. // 使用传统方式读取配置作为对比
  290. var section = cfg.GetSection("Database");
  291. Console.WriteLine("\n使用传统方式读取配置(对比):");
  292. Console.WriteLine($" Host: {section.Get("Host") ?? "(未配置)"}");
  293. Console.WriteLine($" Port: {section.Get("Port") ?? "(未配置)"}");
  294. Console.WriteLine($" Name: {section.Get("Name") ?? "(未配置)"}");
  295. Console.WriteLine("\n源生成器的优势:");
  296. Console.WriteLine(" ✓ 零反射 - 编译时生成绑定代码");
  297. Console.WriteLine(" ✓ Native AOT 兼容 - 完全支持 AOT 发布");
  298. Console.WriteLine(" ✓ 编译时类型检查 - 属性名错误会在编译时报错");
  299. Console.WriteLine(" ✓ 更好的性能 - 无运行时反射开销");
  300. Console.WriteLine(" ✓ IntelliSense 支持 - IDE 自动补全");
  301. await Task.CompletedTask;
  302. }
  303. }