feat(cli): 配置相对路径按 exe 目录解析
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,14 +17,8 @@ public class Program
|
||||
{
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
// 1. 构建引导配置(用于 Serilog 初始化)
|
||||
var bootstrapConfig = new ConfigurationBuilder()
|
||||
.SetBasePath(AppContext.BaseDirectory)
|
||||
.AddJsonFile("appsettings.json", optional: false)
|
||||
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", optional: true)
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args)
|
||||
.Build();
|
||||
// 1. 构建引导配置(用于 Serilog 初始化),并将数据库/日志等相对路径解析到 exe 所在目录
|
||||
var bootstrapConfig = BuildConfiguration(args);
|
||||
|
||||
// 2. 创建引导日志器
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
@@ -89,4 +83,79 @@ public class Program
|
||||
await Log.CloseAndFlushAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建配置。除支持 json/环境变量/命令行外,
|
||||
/// 将 appsettings.json 中数据库、日志、输出等相对路径统一解析为基于 exe 所在目录的绝对路径,
|
||||
/// 保证应用从任意工作目录启动时,配置文件、数据库、日志都落在 exe 旁边。
|
||||
/// </summary>
|
||||
private static IConfiguration BuildConfiguration(string[] args)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(AppContext.BaseDirectory)
|
||||
.AddJsonFile("appsettings.json", optional: false)
|
||||
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production"}.json", optional: true)
|
||||
.AddEnvironmentVariables()
|
||||
.AddCommandLine(args);
|
||||
|
||||
var config = builder.Build();
|
||||
|
||||
// 收集需要解析的相对路径,通过内存配置覆盖层注入(优先级高于 json/环境变量/命令行)
|
||||
var overrides = ResolveRelativePaths(config);
|
||||
|
||||
if (overrides.Count == 0)
|
||||
return config;
|
||||
|
||||
return new ConfigurationBuilder()
|
||||
.AddConfiguration(config)
|
||||
.AddInMemoryCollection(overrides)
|
||||
.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将配置中的相对路径解析为基于 exe 目录的绝对路径。
|
||||
/// </summary>
|
||||
private static Dictionary<string, string?> ResolveRelativePaths(IConfiguration config)
|
||||
{
|
||||
var baseDirectory = AppContext.BaseDirectory;
|
||||
var overrides = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// 数据库文件
|
||||
foreach (var key in new[] { "Database:Path", "Database:LocalPath", "Database:ReleasePath" })
|
||||
{
|
||||
AddResolved(overrides, config, key, baseDirectory);
|
||||
}
|
||||
|
||||
// 日志与输出目录
|
||||
foreach (var key in new[] { "Paths:LogDir", "Paths:OutputDir" })
|
||||
{
|
||||
AddResolved(overrides, config, key, baseDirectory);
|
||||
}
|
||||
|
||||
// Serilog 文件日志路径(遍历 WriteTo 数组,避免硬编码索引)
|
||||
foreach (var sink in config.GetSection("Serilog:WriteTo").GetChildren())
|
||||
{
|
||||
if (!string.Equals(sink["Name"], "File", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
AddResolved(overrides, config, $"{sink.Path}:Args:path", baseDirectory);
|
||||
}
|
||||
|
||||
return overrides;
|
||||
}
|
||||
|
||||
private static void AddResolved(
|
||||
Dictionary<string, string?> overrides,
|
||||
IConfiguration config,
|
||||
string key,
|
||||
string baseDirectory)
|
||||
{
|
||||
var value = config[key];
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return;
|
||||
|
||||
overrides[key] = Path.IsPathRooted(value)
|
||||
? value
|
||||
: Path.GetFullPath(Path.Combine(baseDirectory, value));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user