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

使用Timer定时刷新缓存时触发Cannot access a disposed context异常

Entity Framework定时刷新缓存时上下文已释放的问题解决

错误原因

默认情况下,AddDbContext注册的PostgreSqlContext是Scoped生命周期——DI容器会为每个作用域创建一个实例,作用域结束后自动释放。而你的SomeLogic如果是Singleton(因为Timer长期运行,这类服务通常注册为Singleton),构造函数注入的IProductRepository会持有同一个Scoped的PostgreSqlContext实例。第一次调用BuildCache时上下文正常使用,之后DI容器释放了这个Scoped实例,Timer第二次触发时就会访问已被释放的上下文,抛出异常。

解决方案:每次刷新时创建新的作用域

不要在Singleton服务中直接持有Scoped服务的实例,而是注入IServiceProvider,每次需要访问仓储时创建新的作用域,获取全新的上下文实例:

修改SomeLogic代码:

public class SomeLogic
{
    private readonly IServiceProvider _serviceProvider;
    private Timer _timer;

    public SomeLogic(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;

        BuildCache();
        _timer = new Timer(ReloadHandler, null, 60000, Timeout.Infinite);
    }

    private void ReloadHandler(object state = null)
    {
        BuildCache();
        _timer.Change(60000, Timeout.Infinite);
    }

    public void BuildCache()
    {
        using (var scope = _serviceProvider.CreateScope())
        {
            var productRepository = scope.ServiceProvider.GetRequiredService<IProductRepository>();
            var products = productRepository.GetAllProducts();
            // 处理缓存逻辑
        }
    }
}
  • CreateScope()会创建一个新的DI作用域,作用域内的Scoped服务(包括PostgreSqlContext)会被重新实例化
  • using语句会自动释放作用域,确保上下文被正确回收,避免资源泄漏

其他定时刷新缓存的实现方式

1. 使用.NET自带的BackgroundService(推荐)

BackgroundService是.NET官方提供的后台服务框架,比手动使用Timer更规范,自带生命周期管理和取消支持:

public class CacheRefreshService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<CacheRefreshService> _logger;

    public CacheRefreshService(IServiceProvider serviceProvider, ILogger<CacheRefreshService> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // 首次执行
        await RefreshCacheAsync();

        while (!stoppingToken.IsCancellationRequested)
        {
            // 等待5分钟后再次执行
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
            await RefreshCacheAsync();
        }
    }

    private async Task RefreshCacheAsync()
    {
        try
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var productRepository = scope.ServiceProvider.GetRequiredService<IProductRepository>();
                var products = productRepository.GetAllProducts();
                // 处理缓存逻辑
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "缓存刷新任务执行失败");
        }
    }
}

在Startup中注册后台服务:

services.AddHostedService<CacheRefreshService>();

2. 使用第三方定时任务库(如Hangfire)

如果需要更复杂的定时规则(CRON表达式)、重试机制或可视化管理,可以使用Hangfire这类库。它支持多种存储(包括PostgreSQL),能轻松实现复杂的定时任务调度,适合业务场景复杂的系统。


内容的提问来源于stack exchange,提问作者Вася

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 22:50:30