You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 6 WebAPI仅3个PostEvictionCallbacks触发异常求助

问题分析

你的问题核心在于每次调用SetCache<T>时都创建了新的PhysicalFileProvider局部实例,且没有保留对它的引用。当.NET垃圾回收器(GC)回收这个局部的PhysicalFileProvider后,它对应的文件变更监听会失效,导致关联的缓存项无法收到文件变更通知,自然不会触发PostEvictionCallback。

从日志可见,只有Wind、WeatherInfo、DataSlice三个键的回调被触发,这大概率是因为这三个对应的PhysicalFileProvider实例暂时未被GC回收,而另外三个(Rain、Humi、Temp)的实例已被回收,失去了监听能力。

解决方案

需要确保PhysicalFileProvider的生命周期与应用一致,避免被GC回收。推荐通过依赖注入注册单例的方式实现,符合.NET设计规范。

修改后的代码示例

1. 注册PhysicalFileProvider为单例(Program.cs)

在Program.cs的服务注册段添加:

builder.Services.AddSingleton<IFileProvider>(sp => 
    new PhysicalFileProvider(Service.DataFolderPath));

2. 修改FileService,注入并复用IFileProvider

private readonly IFileProvider _fileProvider;
private readonly IMemoryCache _cache;
private readonly ILogger<FileService> _logger;

// 构造函数注入IFileProvider
public FileService(IMemoryCache cache, ILogger<FileService> logger, IFileProvider fileProvider)
{
    _cache = cache;
    _logger = logger;
    _fileProvider = fileProvider;
}

public void SetCache<T>(string cacheKey, IEnumerable<T> list)
{
    _logger.LogInformation("========= set cachekey: " + cacheKey);
    // 复用注入的FileProvider创建变更令牌
    IChangeToken changeToken = _fileProvider.Watch(Service.DataFileName);
    MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
        .AddExpirationToken(changeToken)
        .RegisterPostEvictionCallback(CacheExpireHandler);

    _cache.Set(cacheKey, list, cacheEntryOptions);

    if (cacheKey == nameof(DataSlice))
    {
        PreviousDataSlice = (IEnumerable<DataSlice>)list;
    }
}

3. 可选优化:共享同一个变更令牌

如果所有缓存项都监听同一个文件,可复用同一个IChangeToken实例减少资源消耗:

private IChangeToken _fileChangeToken;

public FileService(IMemoryCache cache, ILogger<FileService> logger, IFileProvider fileProvider)
{
    _cache = cache;
    _logger = logger;
    _fileProvider = fileProvider;
    // 初始化变更令牌,变更后自动重新监听
    RefreshChangeToken();
}

private void RefreshChangeToken()
{
    _fileChangeToken = _fileProvider.Watch(Service.DataFileName);
    _fileChangeToken.RegisterChangeCallback(_ => RefreshChangeToken(), null);
}

public void SetCache<T>(string cacheKey, IEnumerable<T> list)
{
    _logger.LogInformation("========= set cachekey: " + cacheKey);
    MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
        .AddExpirationToken(_fileChangeToken)
        .RegisterPostEvictionCallback(CacheExpireHandler);

    _cache.Set(cacheKey, list, cacheEntryOptions);

    if (cacheKey == nameof(DataSlice))
    {
        PreviousDataSlice = (IEnumerable<DataSlice>)list;
    }
}
验证逻辑

修改后,所有缓存项都会使用生命周期稳定的PhysicalFileProvider实例创建变更令牌,GC不会回收它。修改SQLite文件后,查看日志即可确认六个缓存键的回调是否全部被触发。

内容的提问来源于stack exchange,提问作者Ching Wang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 04:44:59