下文章介绍如何使用最佳实践、高级功能和实际示例在 .NET 8 中编写更好的配置文件,以优化应用程序的配置过程。
了解 .NET 8 中的配置
.NET 中的配置是将应用程序设置外部化的操作,以便它们变得更容易更改,而无需接触代码库。
这种关注点分离使系统更易于维护,但也支持多种环境和部署方案。
关键组件
-
**配置提供程序:**这些必须从各种来源读取配置值。
-
**配置绑定:**这为配置提供了到 C# 类的直接映射,从而支持强类型并减少运行时错误。
-
**重新加载配置:**更重要的是,对于动态应用程序,应该可以在不重新启动应用程序的情况下重新加载配置。
appsettings.json 的基本结构
下面是一个非常简单的示例,说明了此类 appsettings.json 文件的外观:
{ "AppSettings": { "ApplicationName": "MyApp", "Version": "1.0", "Environment": "Development" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning" } }, "ConnectionStrings": { "DefaultConnection": "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;" }
}
如何写出更好的appsettings.json
改进您的不仅与其结构有关,还与利用 .NET 8 的高级功能有关。 appsettings.json
这是有关改进配置文件的快速操作指南。
1. 对设置进行逻辑分组
对设置进行逻辑分组,这有助于保持可读性和可维护性。将类似的设置存储在一个逻辑组中。使用嵌套 / 子组来传达依赖关系。 例
{ "DatabaseSettings": { "SqlServer": { "ConnectionString": "Server=sqlServerAddress;Database=sqlDatabase;User Id=sqlUser;Password=sqlPassword;" }, "MongoDb": { "ConnectionString": "mongodb://mongoServerAddress:27017", "DatabaseName": "myMongoDb" } }, "ApiSettings": { "BaseUrl": "https://api.example.com", "TimeoutSeconds": 30 }
}
2. 使用强类型配置
将配置设置绑定到 C# 类,提供类型安全和 IntelliSense。如果您的设置名称或类型有拼写错误,它还会提供编译时错误。
例: 声明与设置结构匹配的 C# 类
public class DatabaseSettings
{ public SqlServerSettings SqlServer { get; set; } public MongoDbSettings MongoDb { get; set; }
} public class SqlServerSettings
{ public string ConnectionString { get; set; }
} public class MongoDbSettings
{ public string ConnectionString { get; set; } public string DatabaseName { get; set; }
}
Program.cs 文件中的设置:
var builder = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); }) .ConfigureServices((context, services) => { services.Configure<DatabaseSettings>(context.Configuration.GetSection("DatabaseSettings")); });
3. 应用分层配置
您可以将配置节嵌套在其他配置节中。这在复杂配置的情况下非常有用。
例
{ "Logging": { "LogLevel": { "Default": "Warning", "System": "Error" }, "Console": { "IncludeScopes": true } }
}
映射到 C# 类:
public class LoggingSettings
{ public LogLevelSettings LogLevel { get; set; } public ConsoleSettings Console { get; set; }
} public class LogLevelSettings
{ public string Default { get; set; } public string System { get; set; }
} public class ConsoleSettings
{ public bool IncludeScopes { get; set; }
}
4. 利用特定于环境的配置
如果您的应用程序打算在不同的环境(即 、 和 )下运行,请使用特定于环境的配置文件。DevelopmentStagingProduction
.NET 8 允许您拥有像 appsettings 这样的文件。Development.json 和 appsettings。Production.json,这些设置将覆盖特定环境的 appsettings.json 中的设置。
例
{ "Logging": { "LogLevel": { "Default": "Debug" } }
}
在 Program.cs 中,配置特定于环境的设置:
var builder = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { var env = context.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); });
5. 保护敏感数据
对于敏感数据,如 API 密钥或连接字符串,请勿在 appsettings.json 中提交纯文本。请改用 secret 管理器或环境变量。
例: 通过 .NET Secret Manager 工具存储敏感数据
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;"
并从应用程序访问密钥
var connectionString = Configuration.GetConnectionString("DefaultConnection");
6. 利用配置验证
配置验证可确保您的配置值在使用之前有效。该功能在应用程序生命周期的早期可用于检测错误。
**例:**设置验证规则
public class DatabaseSettings
{ public SqlServerSettings SqlServer { get; set; } public void Validate() { if (string.IsNullOrEmpty(SqlServer?.ConnectionString)) { throw new ArgumentException("SQL Server connection string is required."); } }
}
在应用程序启动时验证配置:
public class Program
{ public static void Main(string[] args) { var host = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); }) .ConfigureServices((context, services) => { var configuration = services.BuildServiceProvider().GetRequiredService<IConfiguration>(); var databaseSettings = configuration.GetSection("DatabaseSettings").Get<DatabaseSettings>(); databaseSettings.Validate(); services.AddSingleton(databaseSettings); }) .Build(); host.Run(); }
}
7. 利用配置重新加载进行运行时更新
如果应用程序必须在不重新启动的情况下更新配置,请利用 ASP.NET Core Configuration 提供的配置重新加载功能。这通常适用于频繁更改的设置。
**例:**配置 appsettings.json 以在更改时重新加载配置:
var builder = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); });
这将确保对 appsettings.json 文件所做的更改会自动重新加载配置,并反映动态更改。
8. 实现自定义配置提供程序
在需要对如何检索或处理配置值进行更多控制的情况下,实施自定义配置提供程序会有所帮助。当需要与 .NET 不支持的外部系统或格式集成时,这可能很有用。
**例:**实现自定义配置提供程序
public class CustomConfigurationProvider : ConfigurationProvider
{ public override void Load() { // Custom logic to load configuration data Data.Add("CustomKey", "CustomValue"); }
} public class CustomConfigurationSource : IConfigurationSource
{ public IConfigurationProvider Build(IConfigurationBuilder builder) { return new CustomConfigurationProvider(); }
}
在 Program.cs 文件中注册您的自定义提供程序:
var builder = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { config.Add(new CustomConfigurationSource()); });
9. 将配置快照用于不可变设置
当您的配置设置是不可变的 — 这意味着它们在运行时不会更改时,您可能需要考虑使用配置的快照。这有助于有效地进行设置管理,以保持整个应用程序的一致性。
例: 配置您的服务以使用快照
public class MySettings
{ public string MyValue { get; set; }
} public static class ServiceCollectionExtensions
{ public static IServiceCollection AddMySettings(this IServiceCollection services, IConfiguration configuration) { services.Configure\<MySettings>(configuration.GetSection("MySettings")); services.AddSingleton(resolver => resolver.GetRequiredService\<IOptionsSnapshot\<MySettings>>().Value); return services; }
}
10. 使用内置功能标志
您可以启用或禁用某些功能,而无需部署新代码。.NET 8 通过 Microsoft.FeatureManagement 等库进行功能管理。
**例:**安装 Feature Management 包
dotnet add package Microsoft.FeatureManagement
在 appsettings.json 中配置功能标志:
{ "FeatureManagement": { "NewFeature": true }
}
在应用程序中使用功能标志:
public class MyService
{ private readonly IFeatureManager _featureManager; public MyService(IFeatureManager featureManager) { _featureManager = featureManager; } public async Task DoWorkAsync() { if (await _featureManager.IsEnabledAsync("NewFeature")) { // Feature is enabled } else { // Feature is not enabled } }
}
11. 基于数据注释的配置验证
可以在配置类上使用 DataAnnotations。这样,您就可以在尝试使用配置值之前确保它们满足已知条件。
**例:**使用 DataAnnotations 定义配置类
public class ApiSettings
{ [Required] public string BaseUrl { get; set; } [Range(1, 60)] public int TimeoutSeconds { get; set; }
}
在启动时设置配置时启用验证:
public class Program
{ public static void Main(string[] args) { var host = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); }) .ConfigureServices((context, services) => { var configuration = services.BuildServiceProvider().GetRequiredService<IConfiguration>(); var apiSettings = configuration.GetSection("ApiSettings").Get<ApiSettings>(); var validationContext = new ValidationContext(apiSettings); Validator.ValidateObject(apiSettings, validationContext, validateAllProperties: true); services.AddSingleton(apiSettings); }) .Build(); host.Run(); }
}
12. 合并多个配置源
为了使配置更灵活、更完整,必须合并多个配置源。换句话说,设置由appsettings.json、环境变量和用户密钥组合而成。
**例:**合并 Program.cs 中的配置源
var builder = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true) .AddEnvironmentVariables() .AddUserSecrets<Program>(); });
13. 文档配置设置
可以添加注释或维护外部文档。appsettings.json
例: 可以将注释添加到appsettings.json。JSON 本身不支持注释。
可以使用外部工具或通过预处理 JSON 文件来支持注释
{ "Logging": { "LogLevel": { "Default": "Information", // Default logging level "Microsoft": "Warning" // Logging level for Microsoft libraries } }
}
14. 配置库
例如,Serilog.Settings.Configuration 可用于维护日志记录配置。
例: 添加库
dotnet add package Serilog.Settings.Configuration
在 appsettings.json 配置 Serilog:
{ "Serilog": { "MinimumLevel": { "Default": "Information", "Override": { "Microsoft": "Warning" } }, "WriteTo": [ { "Name": "Console" } ] }
}
在应用程序中使用 Serilog
public static class Program
{ public static void Main(string[] args) { Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(Configuration) .CreateLogger(); try { Log.Information("Starting up"); CreateHostBuilder(args).Build().Run(); } catch (Exception ex) { Log.Fatal(ex, "Application start-up failed"); throw; } finally { Log.CloseAndFlush(); } }
}