c# 无真正的文件系统事务,因微软弃用 ntfs 事务(txf),改用 atomicwrite 模式:同卷内临时写+原子重命名;多文件需应用层回滚协议,无法保证外部竞态与元数据一致性。

为什么 C# 没有真正的文件系统事务
Windows 文件系统(NTFS)本身支持事务(TxF),但 .NET 从 .NET Framework 4.0 开始就 移除了对 TxF 的托管封装,到 .NET Core/.NET 5+ 更是完全不提供任何公开 API。微软明确弃用 TxF,因其存在严重可靠性问题(如崩溃后事务日志可能损坏、与备份软件冲突、远程文件系统不支持等)。所以你无法在现代 C# 中调用 KernelTransaction 或 TransactedFile 这类类——它们早已被标记为 obsolete 且不可用。
用 AtomicWrite 模式替代单文件“原子写”
对单个文件,可通过“写临时文件 + 原子重命名”逼近原子性。Windows 上 File.Move() 在同卷内是原子的(底层调用 MoveFileEx + MOVEFILE_REPLACE_EXISTING),这是最可靠手段:
string tempPath = Path.GetTempFileName();
try
{
File.WriteAllText(tempPath, content);
File.Move(tempPath, targetPath, true); // 同卷下原子覆盖
}
catch
{
if (File.Exists(tempPath)) File.Delete(tempPath);
throw;
}- 必须确保
tempPath和targetPath在同一 NTFS 卷,跨卷Move会降级为复制+删除,不原子 - 不要用
File.Replace():它虽名字像事务,但实际是先复制再删原文件,且异常时易留残留 - 若需校验,应在
Move前计算哈希写入临时文件旁,Move后立即验证目标文件
多文件操作只能靠“可回滚的分步协议”
没有底层事务支撑时,“多文件原子性”本质是应用层协议:预检查 → 写临时 → 校验 → 批量提交 → 清理。关键不是“同时生效”,而是“失败时彻底还原”:
- 所有目标路径先调用
File.Exists()和File.GetAttributes()检查是否可写、是否只读/隐藏 - 每个文件都写到独立临时路径(如
target.ext.tmp),并记录原始文件是否存在、是否为空 - 全部写完后,用
File.Move()逐个覆盖目标;任一失败则触发回滚:对已覆盖的文件,用之前保存的原始副本(或File.Delete()若原文件不存在)还原 - 把操作步骤和时间戳记入轻量日志文件(如 JSON),崩溃重启后可扫描日志决定继续提交或回滚
注意:回滚本身也可能失败(磁盘满、权限丢失),所以日志必须写在操作前,且日志文件本身也需用 AtomicWrite 模式写入。
第三方库如 KellermanSoftware.AtomicWrite 只是封装了上述模式
这类 NuGet 包没魔法,只是帮你自动管理临时文件、回滚逻辑和日志。例如 AtomicWriteManager.WriteFiles() 内部仍是逐个 Move + 异常时遍历 PreviousState 回退。它不能解决根本限制:
- 无法防止外部进程在“写临时”和“Move”之间修改目标文件(竞态仍存在,只是窗口极短)
- 不处理符号链接、硬链接、ACL 权限变更等元数据操作
- 若程序在
Move后、删除临时文件前崩溃,磁盘会残留临时文件——需额外清理策略
真正棘手的永远是“部分成功后如何定义一致状态”,比如一个配置文件更新必须配套更新三个二进制资源,那业务层就得明确定义:旧配置+新资源是否允许共存?还是必须全有或全无?这个语义没法交给文件系统。










