调用ServerManager.CommitChanges()修改applicationHost.config报错如何解决?
根本原因
该异常的核心是IIS核心配置文件applicationHost.config发生了跨进程/跨实例的写入冲突:
- 每次实例化
ServerManager时,Microsoft.Web.Administration(简称MWA)会在内存中加载一份当前applicationHost.config的快照 - 调用
CommitChanges()时,MWA会先比对内存快照和磁盘文件的哈希值,如果期间有其他操作(比如IIS管理器手动修改配置、其他部署脚本执行、服务器自动配置同步任务、甚至同程序其他ServerManager实例并行写入)修改了该文件,就会触发哈希校验失败,抛出该异常 - 你之前尝试的单例
ServerManager方案无效,是因为单例加载快照后,只要外部其他进程修改了配置文件,提交时依然会触发冲突,单例本身无法解决跨进程的写入竞争问题
正确修复方案
1. 加入写入重试机制(最通用的轻量解决方案)
不要捕获异常后直接执行iisreset(该操作会重启整个IIS服务,中断所有站点请求,生产环境影响极大),改为捕获冲突异常后销毁当前ServerManager实例,延迟小段时间后重新实例化、重新执行所有配置修改逻辑再提交,重试3-5次基本可以覆盖99%的偶发冲突场景,示例代码如下:
int retryCount = 0; const int maxRetry = 5; bool commitSuccess = false; while (retryCount < maxRetry && !commitSuccess) { try { using (ServerManager serverManager = new ServerManager()) { var site = serverManager.Sites.SingleOrDefault(s => s.Name == siteName); // 站点创建、应用池配置等所有修改逻辑全部放到该作用域内,每次重试重新执行 serverManager.CommitChanges(); commitSuccess = true; } } catch (System.IO.FileLoadException ex) when (ex.Message.Contains("the file has changed on disk")) { retryCount++; if (retryCount >= maxRetry) throw; // 等待100-500ms再重试,避开其他进程的写入窗口 System.Threading.Thread.Sleep(200); } }
2. 排查冲突来源(根治方案)
- 检查服务器上是否存在其他定时任务、部署工具、运维脚本会定期修改IIS配置,尽量错开执行时间,或者将所有IIS配置修改操作收敛到同一个入口执行
- 确认程序内部是否存在多线程并行执行IIS配置修改的逻辑,如果有则增加进程内互斥锁(比如
lock语句)避免同程序多实例并发写入
3. 额外优化点
- 不要长期持有
ServerManager实例,每次修改配置都新建实例、用完立即释放,避免内存快照和磁盘文件的版本差距过大 - 不要全局共享
ServerManager实例,不同修改操作各自创建独立实例即可
内容的提问来源于stack exchange,提问作者Howard Chang
相关产品推荐
相关产品推荐

