C#怎么实现配置文件读写_C#如何读取App.config和appsettings.json【教程】

开门见山,先说一个核心结论:App.config 在现代 .NET 开发中已经出局,而 appsettings.json 虽然是标准,但它的设计初衷是“只读”的。 这意味着,如果你想让应用在运行时动态修改配置并持久化保存,指望框架自动帮你双向同步,那可就找错方向了。真正的解决方案,是手动构建配置对象并直接操作 JSON 文件本身。
App.config 在现代 C# 项目里根本不能“写”
首先得明确,App.config 是 .NET Framework 时代的产物。那时候,大家习惯用 ConfigurationManager.AppSettings[“key”] 来读取配置,感觉挺方便。但问题在于,这个机制从设计上就是只读的。你或许尝试过修改值然后调用 Sa ve(),结果发现要么不生效,要么只在当前进程内存里变了一下,应用一重启,改动就全丢了。
到了 .NET Core / .NET 5+ 的时代,情况就更彻底了:
- 新建的 .NET 6/7/8 项目,默认根本不会包含 App.config 文件。
- 即便你手动添加一个,运行时也不会自动加载它。
- 更关键的是,即便你强行想用旧方法,比如调用
ConfigurationManager.OpenExeConfiguration,在 .NET Core+ 平台上,等待你的很可能是一个PlatformNotSupportedException异常。Visual Studio 编译时确实会把 App.config 复制为MyApp.exe.config,但运行时根本不会监听这个文件的任何变更。
所以,结论很清晰:对于新项目,App.config 这条路可以直接放弃了。
appsettings.json 只能读,不能直接写
既然 App.config 不行,那转向标准的 appsettings.json 总可以了吧?很遗憾,这里也有一个常见的误解。我们通过依赖注入拿到的 IConfiguration 接口,它提供的是 GetSection()、GetValue 等一系列读取方法,但翻遍文档,你也找不到一个官方的 Set() 或 Sa ve() 方法。
这是为什么呢?因为 IConfiguration 本质上是一个配置源的只读快照。它的背后可能串联着 JSON 文件、环境变量、命令行参数、内存字典等多个来源。这种设计是为了灵活性和优先级合并,但也意味着它没有“写回”某个特定源(比如 JSON 文件)的职责。
- 如果你试图通过一些“黑魔法”(比如强制转换类型)去修改
IConfiguration实例内部的状态,这属于未公开的行为,不同版本的 .NET SDK 可能导致不可预知的后果,甚至程序崩溃。 - 当然,你可以手动用
JsonSerializer.Serialize()把配置写回 JSON 文件,但这意味着你必须自己处理一堆麻烦事:并发写入冲突、保持 JSON 的格式缩进、处理注释丢失等等。 - 还有一个棘手的问题:如果你的配置项混合了多个来源(比如数据库连接字符串来自 appsettings.json,而日志级别来自环境变量),那么“写回文件”这个操作本身就无法还原这种复杂的来源逻辑。
所以,appsettings.json 本身不是用来“写”的,IConfiguration 也不是那个负责持久化的对象。
真要实现“可写配置”,得绕过 IConfiguration 直接操作 JSON 文件
那么,正确的姿势到底是什么?答案是:绕过 IConfiguration 的只读抽象层,直接对 appsettings.json 这个物理文件进行读写操作。 这是最可控、最可预测的方法。
具体步骤可以分解如下:
- 读取与解析:首先,使用
File.ReadAllText(“appsettings.json”)将文件内容完整读入,然后通过System.Text.Json或Newtonsoft.Json将其反序列化。你可以反序列化为灵活的JsonDocument,或者更常见的,映射到一个预先定义好的 POCO(普通C#类)模型上。 - 修改配置:在内存中修改这个模型对象的属性值,比如
settings.Logging.LogLevel.Default = “Debug”;。 - 写回与重载:使用 JSON 序列化器将修改后的对象重新序列化为字符串,并用
File.WriteAllText写回原文件。这里有个关键点:为了保持文件可读性,建议在序列化时设置WriteIndented = true。写入完成后,调用之前保存的IConfigurationRoot.Reload()方法,触发配置系统重新加载文件,使改动立即生效。
// 构建配置时,确保获取到 IConfigurationRoot 的引用
var config = new ConfigurationBuilder()
.AddJsonFile(“appsettings.json”, optional: false, reloadOnChange: true)
.Build();
var root = (IConfigurationRoot)config;
// 直接读写 JSON 文件
var settings = JsonSerializer.Deserialize(File.ReadAllText(“appsettings.json”));
settings.Logging.LogLevel.Default = “Debug”;
File.WriteAllText(“appsettings.json”, JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true }));
// 触发配置重载
root.Reload();
需要提醒的是,在写回文件前,强烈建议对原文件进行备份。这样可以防止万一序列化过程出错导致 JSON 格式损坏,从而避免应用下次启动时直接失败。
别忽略 reloadOnChange 和文件权限这两个硬限制
即使你按照上面的流程操作,还有两个底层限制必须心里有数,它们常常在部署后成为“幽灵问题”。
第一个是关于 reloadOnChange: true。这个选项依赖于 FileSystemWatcher 来监听文件变化,但它并非百分百可靠:
- 在 Docker 容器环境中,尤其是 Windows 主机搭配 Linux 容器时,挂载卷(Volume)的文件变更事件经常无法正确触发。
- 如果 appsettings.json 文件被其他进程(如 Visual Studio 正在编辑,或者记事本没有关闭)以独占方式打开,重载可能会静默失败。
第二个,也是更实际的限制,是文件系统权限。
- 在 Linux 系统上部署时,如果运行应用的用户(比如 www-data)没有对 appsettings.json 文件的读取权限,应用在启动阶段就会抛出
UnauthorizedAccessException,根本到不了运行时。 - 而“写”配置时,问题更突出:appsettings.json 通常位于应用程序集相同的目录。在生产环境中,无论是 Linux 的 systemd 服务还是 Windows 服务,其运行账户默认往往没有对该目录的写入权限。如果你没提前配置好权限,写文件时会直接抛出异常。
所以,在实现可写配置功能的同时,务必把权限检查和异常处理纳入考量范围,这才是真正稳健的做法。
