如何为IO文件存储实现带自动回滚的自定义事务机制?
通用文件存储事务回滚实现方案
问题背景
现有文件存储接口及基础使用代码如下:
public interface IFileStorage { void Save(string key); void Delete(string key); }
var fileStorage = new FileStorage(); var files = new List<string>(); foreach(var file in files) { fileStorage.Save(file); }
当前实现存在缺陷:如果循环执行中途程序崩溃,已保存的文件会残留,没有回滚机制。我们需要实现一种类似事务的通用解决方案,通过using块包裹操作,一旦范围内发生错误,自动删除已保存的文件完成回滚,目标使用形式如下:
using (var transaction = new CustomFileStorageTransaction()) { var fileStorage = new FileStorage(); var files = new List<string>(); foreach (var file in files) { fileStorage.Save(file); } }
解决方案实现
1. 事务核心类实现
基于IDisposable特性,在using块结束时自动判断是否需要回滚,同时追踪事务内保存的所有文件key:
public class CustomFileStorageTransaction : IDisposable { private readonly List<string> _savedKeys = new List<string>(); private bool _isCommitted = false; // 注册已成功保存的文件key public void TrackSavedKey(string key) { if (!_isCommitted) { _savedKeys.Add(key); } } // 手动提交事务:所有操作完成后调用,跳过后续回滚逻辑 public void Commit() { _isCommitted = true; _savedKeys.Clear(); } public void Dispose() { if (!_isCommitted) { // 执行回滚:删除所有已保存的文件 using var storage = new FileStorage(); foreach (var key in _savedKeys) { try { storage.Delete(key); } catch (Exception ex) { // 记录回滚异常,避免单个文件失败导致全部回滚中断 Console.WriteLine($"回滚文件 {key} 失败: {ex.Message}"); } } } } }
2. 装饰器包装原存储实现
用装饰器模式包裹IFileStorage,无需修改原FileStorage代码,自动将保存的key注册到事务中:
public class TransactionalFileStorage : IFileStorage { private readonly IFileStorage _innerStorage; private readonly CustomFileStorageTransaction _transaction; public TransactionalFileStorage(IFileStorage innerStorage, CustomFileStorageTransaction transaction) { _innerStorage = innerStorage; _transaction = transaction; } public void Save(string key) { _innerStorage.Save(key); _transaction.TrackSavedKey(key); } public void Delete(string key) { _innerStorage.Delete(key); } }
3. 最终使用示例
using (var transaction = new CustomFileStorageTransaction()) { var fileStorage = new TransactionalFileStorage(new FileStorage(), transaction); var files = new List<string> { "doc1.pdf", "photo2.jpg", "data3.csv" }; try { foreach (var file in files) { fileStorage.Save(file); // 模拟中途出错场景 if (file == "photo2.jpg") { throw new IOException("保存文件时发生IO错误"); } } // 所有操作无异常,提交事务 transaction.Commit(); } catch (Exception ex) { Console.WriteLine($"操作失败: {ex.Message}"); // 无需手动回滚,using块结束时会自动执行 } }
方案优势
- 通用性强:基于装饰器和事务类的组合,不侵入原
FileStorage实现,符合开闭原则。 - 自动回滚:利用
IDisposable的特性,using块结束时自动处理回滚逻辑,无需手动编写try/catch重复代码。 - 可靠性高:回滚时单个文件删除失败不影响其他文件,同时保留异常记录便于排查。
内容的提问来源于stack exchange,提问作者Nick Farsi
相关产品推荐
相关产品推荐

