在 .NET 中编写更好的配置文件

科技   2024-10-17 06:03   上海  


以下文章介绍如何使用最佳实践、高级功能和实际示例在 .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();
}
}
}

如果你喜欢我的文章,请给我一个赞!谢谢

架构师老卢
资深软件架构师, 分享编程、软件设计经验, 教授前沿技术, 分享技术资源(每天发布电子书),每天进步一点点...
 最新文章