.NET Core 5.0文件变更未触发内存缓存自动过期问题咨询
问题原因
- 路径适配错误:
IFileProvider.Watch()方法仅支持接收相对于自身根目录的相对路径,且要求路径分隔符为正斜杠。如果传入的strFileName是绝对路径、使用反斜杠作为分隔符,或者和IFileProvider实例的根目录映射范围不匹配(比如用WebRootFileProvider监听内容根目录下的文件),就会导致监听不到文件变更,变更令牌不会触发,缓存自然不会自动过期。 - 文件句柄占用:
XmlDocument.Load()默认以独占读取模式打开文件,会阻塞文件系统的变更事件通知,导致IFileProvider无法捕获到文件修改动作。 - 未配置失效校验逻辑(非核心原因,但会提升调试难度):原有代码没有注册缓存失效的回调逻辑,无法直观判断变更令牌是否正常触发。
修复方案
1. 统一路径处理
先将传入的文件路径转换为IFileProvider兼容的格式,如果是绝对路径,先转为相对于IFileProvider根目录的相对路径,同时统一替换分隔符为正斜杠。如果需要监听任意绝对路径的文件,可以为对应目录单独构造PhysicalFileProvider实例。
2. 优化文件读取逻辑
使用带共享读写权限的文件流加载XML,避免占用文件句柄阻塞变更通知。
3. 补充缓存校验逻辑(可选)
可以根据业务需要保留滑动过期配置,同时可补充缓存失效回调用于调试或预加载。
修复后完整代码
public XmlDocument loadXML(string strFileName) { XmlDocument xml; // 先尝试从缓存获取 if (_cache.TryGetValue(strFileName, out xml)) { return xml; } xml = this.createNewDocument(); // 用共享读写的流加载文件,避免独占锁阻塞变更通知 using (var stream = File.Open(strFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { xml.Load(stream); } if (xml != null) { // 转换为IFileProvider兼容的相对路径 string relativePath = strFileName; if (_fileProvider is PhysicalFileProvider physicalProvider) { relativePath = Path.GetRelativePath(physicalProvider.Root, strFileName) .Replace(Path.DirectorySeparatorChar, '/'); } var changeToken = _fileProvider.Watch(relativePath); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(5)) .AddExpirationToken(changeToken); // 可选:注册失效回调,方便排查问题 cacheEntryOptions.RegisterPostEvictionCallback((key, value, reason, state) => { if (reason == EvictionReason.TokenExpired) { // 可在这里打印日志确认是文件变更触发的缓存失效 } }); _cache.Set(strFileName, xml, cacheEntryOptions); } return xml; }
如果你的_fileProvider没有正确注入,可在服务注册逻辑中补充配置:
// ASP.NET Core 5 Startup.cs 示例 services.AddSingleton<IFileProvider>(sp => { // 控制台等非Web环境替换为IHostEnvironment var env = sp.GetRequiredService<IWebHostEnvironment>(); return env.ContentRootFileProvider; });
内容的提问来源于stack exchange,提问作者William Walseth
相关产品推荐
相关产品推荐

