记得前面写过一篇关于用反射实现的,这篇是用网友们推荐的Sources Generators技术实现的,效率上确实会好不少,就是代码阅读性会上升一些。你是否厌倦了在项目中手写大量的配置读取代码?每次新增配置项都要写一堆重复的JSON解析逻辑?今天我们来看看如何用C#源代码生成器(Source Generators)彻底解决这个痛点,让配置管理变得如丝般顺滑!
注意:这个方案算是抛转引玉,旨在上次反射方案的改进,实际项目中还没有使用过,其实这一切都是从Java spring引出来的。。。
作为.NET开发者,我们经常遇到这些头疼的问题:
手工劳动繁重:每个配置类都要写重复的读取逻辑
C#// 传统方式:大量重复代码
var config = new ApiConfig();
if (jsonObject.TryGetProperty("baseUrl", out var baseUrlElement))
{
config.BaseUrl = baseUrlElement.GetString();
}
if (jsonObject.TryGetProperty("timeout", out var timeoutElement))
{
config.Timeout = timeoutElement.GetInt32();
}
// ... 更多重复代码
维护成本高:配置结构变化时,需要同步修改多个地方
容错性差:缺少统一的异常处理和默认值设定
开发效率低:每次都要写相似的模板代码
C#源代码生成器是.NET 5引入的强大功能,它能在编译时自动生成代码。就像有个智能助手在你编码时自动帮你写重复的模板代码!
让我们构建一个完整的自动化配置管理系统:
首先创建两个特性来标记我们的配置类和属性:
C#[AttributeUsage(AttributeTargets.Class)]
public class ConfigurationAttribute : Attribute
{
public string? FileName { get; set; }
public string? Section { get; set; }
public bool Required { get; set; } = true;
}
[AttributeUsage(AttributeTargets.Property)]
public class ConfigPropertyAttribute : Attribute
{
public string? Key { get; set; }
public object? DefaultValue { get; set; }
public bool Required { get; set; } = false;
}
创建一个增量源代码生成器,这是性能最优的方式:
C#[Generator]
public class ConfigurationGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 查找标记了ConfigurationAttribute的类
var configClasses = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => IsSyntaxTargetForGeneration(s),
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx))
.Where(static m => m is not null);
// 合并所有配置类信息并生成代码
var compilation = context.CompilationProvider.Combine(configClasses.Collect());
context.RegisterSourceOutput(compilation, static (spc, source) => Execute(source.Left, source.Right!, spc));
}
}
关键技术点:
生成器的核心是根据配置类自动生成加载逻辑:
C#private static void GeneratePropertyAssignment(StringBuilder sb, ConfigurationPropertyInfo property)
{
sb.AppendLine($"if (rootElement.TryGetProperty(\"{property.ConfigKey}\", out var {property.Name.ToLower()}Element))");
sb.AppendLine("{");
// 根据属性类型生成不同的赋值代码
switch (property.Type)
{
case "string":
sb.AppendLine($" config.{property.Name} = {property.Name.ToLower()}Element.GetString();");
break;
case "int":
sb.AppendLine($" config.{property.Name} = {property.Name.ToLower()}Element.GetInt32();");
break;
// 更多类型支持...
}
sb.AppendLine("}");
// 处理默认值和必填验证
if (property.DefaultValue != null)
{
sb.AppendLine($"else {{ config.{property.Name} = {FormatDefaultValue(property.DefaultValue, property.Type)}; }}");
}
}
现在开发者只需要简单地标记配置类:
C#[Configuration(FileName = "api", Required = true)]
public class ApiConfig
{
[ConfigProperty(Key = "baseUrl", Required = true)]
public string BaseUrl { get; set; } = string.Empty;
[ConfigProperty(Key = "timeout", DefaultValue = 5000)]
public int TimeoutMs { get; set; }
[ConfigProperty(Key = "endpoints")]
public string[]? Endpoints { get; set; }
}
使用时只需一行代码:
C#var configManager = new GeneratedConfigurationManager();
configManager.LoadConfigurations("config");
var apiConfig = configManager.GetConfiguration<ApiConfig>();
C#[Configuration(FileName = "database", Section = "Database", Required = true)]
public class DatabaseConfig
{
[ConfigProperty(Key = "connectionString", Required = true)]
public string ConnectionString { get; set; } = string.Empty;
[ConfigProperty(Key = "timeout", DefaultValue = 30)]
public int CommandTimeout { get; set; }
}
C#[Configuration(FileName = "email", Section = "Email", Required = true)]
public class EmailConfig
{
[ConfigProperty(Key = "host", Required = true)]
public string Host { get; set; }
[ConfigProperty(Key = "port", Required = true)]
public int Port { get; set; }
}
C#using System;
using System.Collections.Generic;
using System.Text;
namespace ConfigurationLib.SourceGenerators.Models
{
public class ConfigurationClassInfo
{
public string ClassName { get; set; }
public string Namespace { get; set; }
public string? FileName { get; set; }
public string? Section { get; set; }
public bool Required { get; set; }
public List<ConfigurationPropertyInfo> Properties { get; set; } = new();
}
}
C#using System;
using System.Collections.Generic;
using System.Text;
namespace ConfigurationLib.SourceGenerators.Models
{
public class ConfigurationPropertyInfo
{
public string Name { get; set; }
public string Type { get; set; }
public string ConfigKey { get; set; }
public object? DefaultValue { get; set; }
public bool IsRequired { get; set; }
}
}
C#using System;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using ConfigurationLib.SourceGenerators.Models;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace ConfigurationLib.SourceGenerators
{
[Generator]
public class ConfigurationGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 查找标记了ConfigurationAttribute的类
var configClasses = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => IsSyntaxTargetForGeneration(s),
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx))
.Where(static m => m is not null);
// 合并所有配置类信息
var compilation = context.CompilationProvider.Combine(configClasses.Collect());
// 生成代码
context.RegisterSourceOutput(compilation, static (spc, source) => Execute(source.Left, source.Right!, spc));
}
private static bool IsSyntaxTargetForGeneration(SyntaxNode node)
{
return node is ClassDeclarationSyntax { AttributeLists.Count: > 0 };
}
private static ConfigurationClassInfo? GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
{
var classDeclaration = (ClassDeclarationSyntax)context.Node;
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration) as INamedTypeSymbol;
if (classSymbol == null)
return null;
var configAttribute = classSymbol.GetAttributes()
.FirstOrDefault(attr => attr.AttributeClass?.Name == "ConfigurationAttribute");
if (configAttribute == null)
return null;
var fileName = configAttribute.NamedArguments
.FirstOrDefault(arg => arg.Key == "FileName").Value.Value?.ToString();
var section = configAttribute.NamedArguments
.FirstOrDefault(arg => arg.Key == "Section").Value.Value?.ToString();
var required = configAttribute.NamedArguments
.FirstOrDefault(arg => arg.Key == "Required").Value.Value as bool? ?? true;
var properties = classSymbol.GetMembers()
.OfType<IPropertySymbol>()
.Where(p => p.CanBeReferencedByName && p.SetMethod != null)
.Select(p => new ConfigurationPropertyInfo
{
Name = p.Name,
Type = p.Type.ToDisplayString(),
ConfigKey = GetConfigKey(p),
DefaultValue = GetDefaultValue(p),
IsRequired = GetIsRequired(p)
})
.ToList();
return new ConfigurationClassInfo
{
ClassName = classSymbol.Name,
Namespace = classSymbol.ContainingNamespace.ToDisplayString(),
FileName = fileName,
Section = section,
Required = required,
Properties = properties
};
}
private static string GetConfigKey(IPropertySymbol property)
{
var configPropAttribute = property.GetAttributes()
.FirstOrDefault(attr => attr.AttributeClass?.Name == "ConfigPropertyAttribute");
var key = configPropAttribute?.NamedArguments
.FirstOrDefault(arg => arg.Key == "Key").Value.Value?.ToString();
return key ?? property.Name;
}
private static object? GetDefaultValue(IPropertySymbol property)
{
var configPropAttribute = property.GetAttributes()
.FirstOrDefault(attr => attr.AttributeClass?.Name == "ConfigPropertyAttribute");
return configPropAttribute?.NamedArguments
.FirstOrDefault(arg => arg.Key == "DefaultValue").Value.Value;
}
private static bool GetIsRequired(IPropertySymbol property)
{
var configPropAttribute = property.GetAttributes()
.FirstOrDefault(attr => attr.AttributeClass?.Name == "ConfigPropertyAttribute");
return configPropAttribute?.NamedArguments
.FirstOrDefault(arg => arg.Key == "Required").Value.Value as bool? ?? false;
}
private static void Execute(Compilation compilation, ImmutableArray<ConfigurationClassInfo> configClasses, SourceProductionContext context)
{
if (configClasses.IsDefaultOrEmpty)
return;
var sourceBuilder = new StringBuilder();
// 生成头部
sourceBuilder.AppendLine("using System;");
sourceBuilder.AppendLine("using System.Collections.Generic;");
sourceBuilder.AppendLine("using System.IO;");
sourceBuilder.AppendLine("using System.Text.Json;");
sourceBuilder.AppendLine("using ConfigurationLib;");
sourceBuilder.AppendLine();
sourceBuilder.AppendLine("namespace ConfigurationLib.Generated");
sourceBuilder.AppendLine("{");
// 生成ConfigurationManager类
sourceBuilder.Append(GenerateConfigurationManager(configClasses));
// 生成每个配置类的加载器
foreach (var configClass in configClasses)
{
sourceBuilder.Append(GenerateConfigurationLoader(configClass));
}
// 结束类和命名空间
sourceBuilder.AppendLine(" }"); // 结束 GeneratedConfigurationManager 类
sourceBuilder.AppendLine("}"); // 结束 namespace
context.AddSource("GeneratedConfigurationManager.g.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
}
private static string GenerateConfigurationManager(ImmutableArray<ConfigurationClassInfo> configClasses)
{
var sb = new StringBuilder();
sb.AppendLine(" public class GeneratedConfigurationManager : IConfigurationManager");
sb.AppendLine(" {");
sb.AppendLine(" private readonly Dictionary<Type, object> _configInstances = new();");
sb.AppendLine(" private readonly Dictionary<string, JsonDocument> _configFiles = new();");
sb.AppendLine();
sb.AppendLine(" public void LoadConfigurations(string configDirectory = \"config\")");
sb.AppendLine(" {");
sb.AppendLine(" LoadConfigFiles(configDirectory);");
sb.AppendLine();
// 为每个配置类生成加载代码
foreach (var configClass in configClasses)
{
sb.AppendLine($" Load{configClass.ClassName}Configuration();");
}
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" private void LoadConfigFiles(string configDirectory)");
sb.AppendLine(" {");
sb.AppendLine(" if (!Directory.Exists(configDirectory))");
sb.AppendLine(" return;");
sb.AppendLine();
sb.AppendLine(" var jsonFiles = Directory.GetFiles(configDirectory, \"*.json\", SearchOption.AllDirectories);");
sb.AppendLine(" foreach (var filePath in jsonFiles)");
sb.AppendLine(" {");
sb.AppendLine(" try");
sb.AppendLine(" {");
sb.AppendLine(" var fileName = Path.GetFileNameWithoutExtension(filePath);");
sb.AppendLine(" var jsonContent = File.ReadAllText(filePath);");
sb.AppendLine(" var jsonDocument = JsonDocument.Parse(jsonContent);");
sb.AppendLine(" _configFiles[fileName.ToLower()] = jsonDocument;");
sb.AppendLine(" }");
sb.AppendLine(" catch (Exception ex)");
sb.AppendLine(" {");
sb.AppendLine(" Console.WriteLine($\"Failed to load config file {filePath}: {ex.Message}\");");
sb.AppendLine(" }");
sb.AppendLine(" }");
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" public T GetConfiguration<T>() where T : class, new()");
sb.AppendLine(" {");
sb.AppendLine(" return _configInstances.TryGetValue(typeof(T), out var instance) ? (T)instance : null;");
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" public IEnumerable<object> GetAllConfigurations()");
sb.AppendLine(" {");
sb.AppendLine(" return _configInstances.Values;");
sb.AppendLine(" }");
sb.AppendLine();
return sb.ToString();
}
private static string GenerateConfigurationLoader(ConfigurationClassInfo configClass)
{
var sb = new StringBuilder();
sb.AppendLine($" private void Load{configClass.ClassName}Configuration()");
sb.AppendLine(" {");
sb.AppendLine($" var config = new {configClass.Namespace}.{configClass.ClassName}();");
sb.AppendLine();
// 确定配置文件名
var fileNameExpression = configClass.FileName != null
? $"\"{configClass.FileName.ToLower()}\""
: $"\"{configClass.ClassName.ToLower()}\"";
if (configClass.FileName == null)
{
sb.AppendLine($" var fileName = {fileNameExpression};");
sb.AppendLine(" if (fileName.EndsWith(\"config\"))");
sb.AppendLine(" fileName = fileName.Replace(\"config\", \"\");");
}
else
{
sb.AppendLine($" var fileName = {fileNameExpression};");
}
sb.AppendLine();
sb.AppendLine(" if (!_configFiles.TryGetValue(fileName, out var jsonDocument))");
sb.AppendLine(" {");
if (configClass.Required)
{
sb.AppendLine(" throw new FileNotFoundException($\"Required configuration file not found: {fileName}.json\");");
}
else
{
sb.AppendLine(" _configInstances[typeof(" + configClass.Namespace + "." + configClass.ClassName + ")] = config;");
sb.AppendLine(" return;");
}
sb.AppendLine(" }");
sb.AppendLine();
sb.AppendLine(" var rootElement = jsonDocument.RootElement;");
// 处理Section
if (!string.IsNullOrEmpty(configClass.Section))
{
sb.AppendLine($" if (rootElement.TryGetProperty(\"{configClass.Section}\", out var sectionElement))");
sb.AppendLine(" rootElement = sectionElement;");
if (configClass.Required)
{
sb.AppendLine(" else");
sb.AppendLine($" throw new InvalidOperationException($\"Required section '{configClass.Section}' not found in {{fileName}}.json\");");
}
}
sb.AppendLine();
// 为每个属性生成赋值代码
foreach (var property in configClass.Properties)
{
GeneratePropertyAssignment(sb, property);
}
sb.AppendLine();
sb.AppendLine($" _configInstances[typeof({configClass.Namespace}.{configClass.ClassName})] = config;");
sb.AppendLine(" }");
sb.AppendLine();
return sb.ToString();
}
private static void GeneratePropertyAssignment(StringBuilder sb, ConfigurationPropertyInfo property)
{
sb.AppendLine($" // Setting property: {property.Name}");
sb.AppendLine($" if (rootElement.TryGetProperty(\"{property.ConfigKey}\", out var {property.Name.ToLower()}Element))");
sb.AppendLine(" {");
sb.AppendLine(" try");
sb.AppendLine(" {");
// 根据属性类型生成不同的赋值代码
GenerateTypeSpecificAssignment(sb, property);
sb.AppendLine(" }");
sb.AppendLine(" catch (Exception ex)");
sb.AppendLine(" {");
sb.AppendLine($" Console.WriteLine($\"Error setting property {property.Name}: {{ex.Message}}\");");
sb.AppendLine(" }");
sb.AppendLine(" }");
// 处理默认值
if (property.DefaultValue != null)
{
sb.AppendLine(" else");
sb.AppendLine(" {");
sb.AppendLine($" config.{property.Name} = {FormatDefaultValue(property.DefaultValue, property.Type)};");
sb.AppendLine(" }");
}
else if (property.IsRequired)
{
sb.AppendLine(" else");
sb.AppendLine(" {");
sb.AppendLine($" throw new InvalidOperationException($\"Required property '{property.ConfigKey}' not found in configuration\");");
sb.AppendLine(" }");
}
sb.AppendLine();
}
private static void GenerateTypeSpecificAssignment(StringBuilder sb, ConfigurationPropertyInfo property)
{
var elementVar = $"{property.Name.ToLower()}Element";
// 改进类型判断逻辑
var type = property.Type;
var isNullable = type.EndsWith("?");
var baseType = isNullable ? type.Substring(0, type.Length - 1) : type;
switch (baseType)
{
case "string":
sb.AppendLine($" config.{property.Name} = {elementVar}.GetString();");
break;
case "int":
sb.AppendLine($" config.{property.Name} = {elementVar}.GetInt32();");
break;
case "long":
sb.AppendLine($" config.{property.Name} = {elementVar}.GetInt64();");
break;
case "bool":
sb.AppendLine($" config.{property.Name} = {elementVar}.GetBoolean();");
break;
case "double":
sb.AppendLine($" config.{property.Name} = {elementVar}.GetDouble();");
break;
case "float":
sb.AppendLine($" config.{property.Name} = {elementVar}.GetSingle();");
break;
case "decimal":
sb.AppendLine($" config.{property.Name} = {elementVar}.GetDecimal();");
break;
case "System.DateTime":
case "DateTime":
sb.AppendLine($" config.{property.Name} = {elementVar}.GetDateTime();");
break;
case "System.Guid":
case "Guid":
sb.AppendLine($" config.{property.Name} = {elementVar}.GetGuid();");
break;
default:
// 对于复杂类型和数组,使用JsonSerializer
sb.AppendLine($" config.{property.Name} = JsonSerializer.Deserialize<{property.Type}>({elementVar}.GetRawText());");
break;
}
}
private static string FormatDefaultValue(object defaultValue, string type)
{
if (defaultValue == null)
return "null";
if (type == "string")
return $"\"{defaultValue}\"";
if (type == "bool" || type == "bool?")
return defaultValue.ToString()!.ToLower();
if (type == "char")
return $"'{defaultValue}'";
return defaultValue.ToString()!;
}
}
}
项目配置文件,很重要。
XML<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<IncludeBuildOutput>false</IncludeBuildOutput>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<IsRoslynComponent>true</IsRoslynComponent>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<PackageId>ConfigurationLib.SourceGenerators</PackageId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
</ItemGroup>
</Project>
C#using System;
using System.Collections.Generic;
using System.Text;
namespace ConfigurationLib.Attributes
{
[AttributeUsage(AttributeTargets.Property)]
public class ConfigPropertyAttribute : Attribute
{
public string? Key { get; set; }
public object? DefaultValue { get; set; }
public bool Required { get; set; } = false;
}
}
C#using System;
using System.Collections.Generic;
using System.Text;
namespace ConfigurationLib.Attributes
{
[AttributeUsage(AttributeTargets.Class)]
public class ConfigurationAttribute : Attribute
{
public string? FileName { get; set; }
public string? Section { get; set; }
public bool Required { get; set; } = true;
}
}
C#using System;
using System.Collections.Generic;
using System.Text;
namespace ConfigurationLib
{
public interface IConfigurationManager
{
void LoadConfigurations(string configDirectory = "config");
T GetConfiguration<T>() where T : class, new();
IEnumerable<object> GetAllConfigurations();
}
}
XML<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
C#using AppTest.Configurations;
namespace AppTest
{
internal class Program
{
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.WriteLine("=== Configuration Management Demo ===");
Console.WriteLine();
try
{
// 创建配置管理器实例
var configManager = new ConfigurationLib.Generated.GeneratedConfigurationManager();
// 加载所有配置文件
configManager.LoadConfigurations("config");
Console.WriteLine("✅ Configurations loaded successfully!");
Console.WriteLine();
// 使用数据库配置
DemonstrateUsage(configManager);
}
catch (Exception ex)
{
Console.WriteLine($"❌ Error loading configurations: {ex.Message}");
Console.WriteLine($"Stack trace: {ex.StackTrace}");
}
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
private static void DemonstrateUsage(ConfigurationLib.Generated.GeneratedConfigurationManager configManager)
{
// 获取数据库配置
var dbConfig = configManager.GetConfiguration<DatabaseConfig>();
if (dbConfig != null)
{
Console.WriteLine("📊 Database Configuration:");
Console.WriteLine($" Connection String: {MaskConnectionString(dbConfig.ConnectionString)}");
Console.WriteLine($" Command Timeout: {dbConfig.CommandTimeout}s");
Console.WriteLine($" Enable Retry: {dbConfig.EnableRetry}");
Console.WriteLine($" Max Retry Count: {dbConfig.MaxRetryCount}");
Console.WriteLine();
// 模拟数据库连接
ConnectToDatabase(dbConfig);
}
// 获取API配置
var apiConfig = configManager.GetConfiguration<ApiConfig>();
if (apiConfig != null)
{
Console.WriteLine("🌐 API Configuration:");
Console.WriteLine($" Base URL: {apiConfig.BaseUrl}");
Console.WriteLine($" API Key: {MaskApiKey(apiConfig.ApiKey)}");
Console.WriteLine($" Timeout: {apiConfig.TimeoutMs}ms");
Console.WriteLine($" Endpoints: [{string.Join(", ", apiConfig.Endpoints ?? new string[0])}]");
Console.WriteLine();
// 模拟API调用
CallApi(apiConfig);
}
// 获取邮箱
var emailConfig = configManager.GetConfiguration<EmailConfig>();
if (emailConfig != null)
{
Console.WriteLine("📊 Email Configuration:");
Console.WriteLine($" Host String: {emailConfig.Host}");
Console.WriteLine($" Port : {emailConfig.Port}s");
Console.WriteLine($" EnableSsl : {emailConfig.EnableSsl}s");
Console.WriteLine();
}
// 获取日志配置
var loggingConfig = configManager.GetConfiguration<LoggingConfig>();
if (loggingConfig != null)
{
Console.WriteLine("📝 Logging Configuration:");
Console.WriteLine($" Log Level: {loggingConfig.LogLevel}");
Console.WriteLine($" File Logging: {loggingConfig.EnableFileLogging}");
Console.WriteLine($" Log Path: {loggingConfig.LogPath}");
Console.WriteLine($" Max File Size: {loggingConfig.MaxFileSizeMB}MB");
Console.WriteLine();
// 配置日志系统
ConfigureLogging(loggingConfig);
}
// 获取功能配置
var featureConfig = configManager.GetConfiguration<FeatureConfig>();
if (featureConfig != null)
{
Console.WriteLine("🚩 Feature Configuration:");
Console.WriteLine($" Experimental Features: {featureConfig.EnableExperimentalFeatures}");
if (featureConfig.Flags != null)
{
Console.WriteLine($" New Feature: {featureConfig.Flags.EnableNewFeature}");
Console.WriteLine($" Beta Features: {featureConfig.Flags.EnableBetaFeatures}");
Console.WriteLine($" Feature Expiry: {featureConfig.Flags.FeatureExpiryDate?.ToString("yyyy-MM-dd") ?? "Not set"}");
}
if (featureConfig.ExperimentalFeaturesList != null)
{
Console.WriteLine($" Experimental List: [{string.Join(", ", featureConfig.ExperimentalFeaturesList)}]");
}
Console.WriteLine();
// 处理功能标志
HandleFeatureFlags(featureConfig);
}
// 显示所有加载的配置
Console.WriteLine("📋 All Loaded Configurations:");
var allConfigs = configManager.GetAllConfigurations();
foreach (var config in allConfigs)
{
Console.WriteLine($" - {config.GetType().Name}");
}
}
private static void ConnectToDatabase(DatabaseConfig config)
{
Console.WriteLine("🔌 Simulating database connection...");
// 这里是模拟代码,实际应用中会创建真实的数据库连接
if (!string.IsNullOrEmpty(config.ConnectionString))
{
Console.WriteLine(" ✅ Database connected successfully!");
Console.WriteLine($" ⏱️ Using timeout: {config.CommandTimeout}s");
if (config.EnableRetry)
{
Console.WriteLine($" 🔄 Retry enabled with max count: {config.MaxRetryCount}");
}
}
else
{
Console.WriteLine(" ❌ Invalid connection string!");
}
Console.WriteLine();
}
private static void CallApi(ApiConfig config)
{
Console.WriteLine("🌍 Simulating API calls...");
if (!string.IsNullOrEmpty(config.BaseUrl))
{
Console.WriteLine($" 📡 Connecting to: {config.BaseUrl}");
Console.WriteLine($" ⏱️ Request timeout: {config.TimeoutMs}ms");
if (config.Endpoints != null)
{
foreach (var endpoint in config.Endpoints)
{
Console.WriteLine($" 📝 Available endpoint: {config.BaseUrl}{endpoint}");
}
}
Console.WriteLine(" ✅ API connection established!");
}
else
{
Console.WriteLine(" ❌ Invalid API base URL!");
}
Console.WriteLine();
}
private static void ConfigureLogging(LoggingConfig config)
{
Console.WriteLine("📋 Configuring logging system...");
Console.WriteLine($" 📊 Log level set to: {config.LogLevel}");
if (config.EnableFileLogging)
{
Console.WriteLine($" 📄 File logging enabled: {config.LogPath}");
Console.WriteLine($" 💾 Max file size: {config.MaxFileSizeMB}MB");
// 确保日志目录存在
var logDir = Path.GetDirectoryName(config.LogPath);
if (!string.IsNullOrEmpty(logDir) && !Directory.Exists(logDir))
{
Directory.CreateDirectory(logDir);
Console.WriteLine($" 📁 Created log directory: {logDir}");
}
}
else
{
Console.WriteLine(" 📺 Console logging only");
}
Console.WriteLine(" ✅ Logging configured successfully!");
Console.WriteLine();
}
private static void HandleFeatureFlags(FeatureConfig config)
{
Console.WriteLine("🎛️ Processing feature flags...");
if (config.Flags != null)
{
if (config.Flags.EnableNewFeature)
{
Console.WriteLine(" 🆕 New feature is ENABLED");
}
if (config.Flags.EnableBetaFeatures)
{
Console.WriteLine(" 🧪 Beta features are ENABLED");
}
else
{
Console.WriteLine(" 🚫 Beta features are DISABLED");
}
if (config.Flags.FeatureExpiryDate.HasValue &&
config.Flags.FeatureExpiryDate.Value < DateTime.Now)
{
Console.WriteLine(" ⚠️ Some features have expired!");
}
}
if (config.EnableExperimentalFeatures && config.ExperimentalFeaturesList != null)
{
Console.WriteLine(" 🧬 Experimental features enabled:");
foreach (var feature in config.ExperimentalFeaturesList)
{
Console.WriteLine($" - {feature}");
}
}
Console.WriteLine(" ✅ Feature flags processed!");
Console.WriteLine();
}
private static string MaskConnectionString(string connectionString)
{
if (string.IsNullOrEmpty(connectionString))
return "[Not Set]";
return connectionString;
}
private static string MaskApiKey(string? apiKey)
{
if (string.IsNullOrEmpty(apiKey))
return "[Not Set]";
return apiKey.Length > 8
? apiKey.Substring(0, 4) + "****" + apiKey.Substring(apiKey.Length - 4)
: "****";
}
}
}
JSON{
"baseUrl": "https://api.example.com",
"apiKey": "your-api-key-here",
"timeout": 10000,
"endpoints": [
"/users",
"/products",
"/orders"
]
}
JSON{
"Database": {
"connectionString": "Server=localhost;Database=MyApp;Integrated Security=true;",
"timeout": 60,
"enableRetry": true,
"maxRetryCount": 5
}
}
JSON{
"flags": {
"EnableNewFeature": true,
"EnableBetaFeatures": false,
"FeatureExpiryDate": "2024-12-31T23:59:59Z"
},
"enableExperimentalFeatures": true,
"experimentalFeaturesList": [
"NewUI",
"EnhancedSearch",
"AIIntegration"
]
}
JSON{
"Logging": {
"level": "Debug",
"enableFileLogging": true,
"logPath": "logs/myapp.log",
"maxFileSizeMB": 50
}
}
JsonDocument
而非JObject
,性能更佳C#// 自动支持数组、对象等复杂类型
[ConfigProperty(Key = "features")]
public FeatureFlags? Flags { get; set; }
[ConfigProperty(Key = "endpoints")]
public string[]? Endpoints { get; set; }
可以扩展生成器支持环境变量覆盖:
C#// 优先级:环境变量 > 配置文件 > 默认值
var envValue = Environment.GetEnvironmentVariable($"APP_{property.ConfigKey.ToUpper()}");
if (!string.IsNullOrEmpty(envValue))
{
config.Property = ParseValue(envValue, property.Type);
}
通过源代码生成器,我们实现了:
这个配置管理神器不仅让代码更简洁,还大大提升了开发体验。你在项目中是否也遇到过类似的配置管理痛点?欢迎在评论区分享你的经验,或者说说你希望看到哪些功能扩展!
如果这篇文章对你有帮助,请点赞并分享给更多的.NET开发同伴,让我们一起用更优雅的方式写代码!
想了解更多C#高级技巧?关注我,带你探索更多编程黑科技!
相关信息
通过网盘分享的文件:AppGeneratorsConfig.zip 链接: https://pan.baidu.com/s/1xYO28L100bEjpHPwz6W92A?pwd=jb4m 提取码: jb4m --来自百度网盘超级会员v9的分享
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!