本页展示如何将 Apq.Cfg 集成到 ASP.NET Core 依赖注入系统。
var builder = WebApplication.CreateBuilder(args);
// 使用 AddApqCfg
builder.Services.AddApqCfg(cfg => cfg
.AddJson("config.json", level: 0, writeable: false)
.AddJson($"config.{builder.Environment.EnvironmentName}.json", level: 1, writeable: false, optional: true)
.AddEnvironmentVariables(level: 2, prefix: "APP_"));
var app = builder.Build();
public class MyService
{
private readonly ICfgRoot _cfg;
public MyService(ICfgRoot cfg)
{
_cfg = cfg;
}
public string GetDatabaseHost()
{
return _cfg.Get("Database:Host") ?? "localhost";
}
}
public class DatabaseOptions
{
public const string SectionName = "Database";
public string Host { get; set; } = "localhost";
public int Port { get; set; } = 5432;
public string Database { get; set; } = "";
public string Username { get; set; } = "";
public string Password { get; set; } = "";
public string ConnectionString =>
$"Host={Host};Port={Port};Database={Database};Username={Username};Password={Password}";
}
public class CacheOptions
{
public const string SectionName = "Cache";
public bool Enabled { get; set; } = true;
public int MaxSize { get; set; } = 1000;
public TimeSpan Expiry { get; set; } = TimeSpan.FromMinutes(5);
}
var builder = WebApplication.CreateBuilder(args);
// 注册配置
builder.Services.AddApqCfg(cfg => cfg
.AddJson("config.json", level: 0, writeable: false)
.AddEnvironmentVariables(level: 1, prefix: "APP_"));
// 绑定选项
builder.Services.ConfigureApqCfg<DatabaseOptions>(DatabaseOptions.SectionName);
builder.Services.ConfigureApqCfg<CacheOptions>(CacheOptions.SectionName);
public class DatabaseService
{
private readonly DatabaseOptions _options;
public DatabaseService(IOptions<DatabaseOptions> options)
{
_options = options.Value;
}
public void Connect()
{
Console.WriteLine($"连接到: {_options.ConnectionString}");
}
}
每次请求重新读取配置:
public class CacheService
{
private readonly IOptionsSnapshot<CacheOptions> _options;
public CacheService(IOptionsSnapshot<CacheOptions> options)
{
_options = options;
}
public void DoWork()
{
// 每次访问获取最新配置
if (_options.Value.Enabled)
{
Console.WriteLine($"缓存大小: {_options.Value.MaxSize}");
}
}
}
支持配置变更通知:
public class ConfigWatcherService : IHostedService, IDisposable
{
private readonly IOptionsMonitor<DatabaseOptions> _options;
private readonly IDisposable _changeListener;
private readonly ILogger<ConfigWatcherService> _logger;
public ConfigWatcherService(
IOptionsMonitor<DatabaseOptions> options,
ILogger<ConfigWatcherService> logger)
{
_options = options;
_logger = logger;
// 监听配置变更
_changeListener = _options.OnChange(OnConfigChanged);
}
private void OnConfigChanged(DatabaseOptions newOptions)
{
_logger.LogInformation("数据库配置已更新: {Host}:{Port}",
newOptions.Host, newOptions.Port);
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("当前数据库: {Host}", _options.CurrentValue.Host);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public void Dispose() => _changeListener?.Dispose();
}
using System.ComponentModel.DataAnnotations;
public class DatabaseOptions
{
[Required(ErrorMessage = "数据库主机不能为空")]
public string Host { get; set; } = "";
[Range(1, 65535, ErrorMessage = "端口必须在 1-65535 之间")]
public int Port { get; set; } = 5432;
[Required]
[MinLength(1, ErrorMessage = "数据库名不能为空")]
public string Database { get; set; } = "";
}
// 注册时启用验证
builder.Services.AddOptions<DatabaseOptions>()
.Bind(builder.Configuration.GetSection("Database"))
.ValidateDataAnnotations()
.ValidateOnStart(); // 启动时验证
builder.Services.AddOptions<DatabaseOptions>()
.Bind(builder.Configuration.GetSection("Database"))
.Validate(options =>
{
// 自定义验证逻辑
if (options.Host.Contains("localhost") && options.Port == 5432)
{
return true; // 开发环境
}
// 生产环境必须使用非默认端口
return options.Port != 5432;
}, "生产环境不能使用默认端口")
.ValidateOnStart();
public class DatabaseOptionsValidator : IValidateOptions<DatabaseOptions>
{
private readonly IHostEnvironment _environment;
public DatabaseOptionsValidator(IHostEnvironment environment)
{
_environment = environment;
}
public ValidateOptionsResult Validate(string? name, DatabaseOptions options)
{
var errors = new List<string>();
if (string.IsNullOrEmpty(options.Host))
errors.Add("数据库主机不能为空");
if (_environment.IsProduction())
{
if (options.Host.Contains("localhost"))
errors.Add("生产环境不能使用 localhost");
if (string.IsNullOrEmpty(options.Password))
errors.Add("生产环境必须设置密码");
}
return errors.Count > 0
? ValidateOptionsResult.Fail(errors)
: ValidateOptionsResult.Success;
}
}
// 注册验证器
builder.Services.AddSingleton<IValidateOptions<DatabaseOptions>, DatabaseOptionsValidator>();
// config.json
{
"Databases": {
"Primary": {
"Host": "primary.db.local",
"Port": 5432
},
"Readonly": {
"Host": "readonly.db.local",
"Port": 5432
}
}
}
// 注册命名选项
builder.Services.Configure<DatabaseOptions>("Primary",
builder.Configuration.GetSection("Databases:Primary"));
builder.Services.Configure<DatabaseOptions>("Readonly",
builder.Configuration.GetSection("Databases:Readonly"));
// 使用命名选项
public class MultiDatabaseService
{
private readonly IOptionsSnapshot<DatabaseOptions> _options;
public MultiDatabaseService(IOptionsSnapshot<DatabaseOptions> options)
{
_options = options;
}
public DatabaseOptions GetPrimaryDb() => _options.Get("Primary");
public DatabaseOptions GetReadonlyDb() => _options.Get("Readonly");
}
var builder = WebApplication.CreateBuilder(args);
// 配置 Apq.Cfg
builder.Services.AddApqCfg(cfg => cfg
.AddJson("config.json", level: 0, writeable: false)
.AddJson($"config.{builder.Environment.EnvironmentName}.json", level: 1, writeable: false, optional: true)
.AddSource(new ConsulCfgSource("http://consul:8500", "myapp/config", level: 10, writeable: false, watch: true, optional: true))
.AddEnvironmentVariables(level: 20, prefix: "APP_"));
// 配置选项
builder.Services.AddOptions<DatabaseOptions>()
.Bind(builder.Configuration.GetSection("Database"))
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddOptions<CacheOptions>()
.Bind(builder.Configuration.GetSection("Cache"));
// 注册服务
builder.Services.AddScoped<DatabaseService>();
builder.Services.AddScoped<CacheService>();
builder.Services.AddHostedService<ConfigWatcherService>();
var app = builder.Build();
app.MapGet("/config", (IOptions<DatabaseOptions> dbOptions, IOptions<CacheOptions> cacheOptions) =>
{
return new
{
Database = new { dbOptions.Value.Host, dbOptions.Value.Port },
Cache = new { cacheOptions.Value.Enabled, cacheOptions.Value.MaxSize }
};
});
app.Run();