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

使用System.Timers.Timer Elapsed事件时DBContext无法获取最新数据

问题

使用结合System.Timers.Timer的代码时,Elapsed事件触发的方法中,通过依赖注入(DI)获取的DBContext无法获取到数据库后台已更新的记录,请问是否需要配置DBContext禁用缓存或进行其他设置?

用户提供的代码:

private System.Timers.Timer aTimer;
private Stopwatch stopWatch;

//MAIN CODE
aTimer = new System.Timers.Timer
{
    Interval = _settings.RetryIntervalsInSeconds * 1000
};
stopWatch = Stopwatch.StartNew();
// Hook up the Elapsed event for the timer. 
aTimer.Elapsed += async (sender, e) =>
    await VerifyRecordIsUpdatedAsync(sender, e, record.Id);

// Have the timer fire repeated events (true is the default)
aTimer.AutoReset = true;

// Start the timer
aTimer.Start();

while (aTimer.Enabled)
{
    var timeRetryExecuting = stopWatch.Elapsed;
    if (timeRetryExecuting.Seconds > _settings.TimeoutWaitingInSeconds)
    {
        aTimer.Stop();
    }
}

// EVENT METHOD
private async Task VerifyRecordIsUpdatedAsync(object sender, 
    ElapsedEventArgs e, Guid recordId)
{
    var record = await _context.Records.FirstOrDefaultAsync(x => x.Id == recordId);
    var timeRetryExecuting = stopWatch.Elapsed;
    if (record.Status != Status.Pending
        || timeRetryExecuting.Seconds > _settings.TimeoutWaitingInSeconds)
    {
        aTimer.Stop();
        stopWatch.Stop();
    }
}
解决方案

这是因为EF Core的DbContext默认自带一级缓存(上下文缓存)——同一个DbContext实例会缓存已经查询过的实体,后续查询会直接返回缓存里的对象,不会去数据库拉取最新数据。你的代码里_context应该是一个长期存在的实例,所以才会一直读不到更新。

你可以通过以下几种方式解决:

  • 使用AsNoTracking()绕过缓存
    在查询时添加AsNoTracking(),强制每次查询都直接访问数据库,不使用缓存:

    var record = await _context.Records.AsNoTracking()
                        .FirstOrDefaultAsync(x => x.Id == recordId);
    
  • 刷新指定实体的状态
    如果需要保留上下文缓存,但要获取单条实体的最新数据,可以调用ReloadAsync()强制刷新:

    var record = await _context.Records.FirstOrDefaultAsync(x => x.Id == recordId);
    if (record != null)
    {
        await _context.Entry(record).ReloadAsync();
    }
    
  • 使用短生命周期的DbContext
    这是最符合EF Core设计理念的方案:DbContext应该是短生命周期的,不要和Timer长期绑定。通过DI注入IServiceScopeFactory,每次在Elapsed事件中创建新的作用域,从中获取DbContext,用完自动释放:

    // 先在类的构造函数中注入IServiceScopeFactory
    private readonly IServiceScopeFactory _scopeFactory;
    
    // 在事件方法中使用:
    private async Task VerifyRecordIsUpdatedAsync(object sender, 
        ElapsedEventArgs e, Guid recordId)
    {
        using var scope = _scopeFactory.CreateScope();
        var scopedContext = scope.ServiceProvider.GetRequiredService<YourDbContext>();
        
        var record = await scopedContext.Records.FirstOrDefaultAsync(x => x.Id == recordId);
        var timeRetryExecuting = stopWatch.Elapsed;
        if (record.Status != Status.Pending
            || timeRetryExecuting.Seconds > _settings.TimeoutWaitingInSeconds)
        {
            aTimer.Stop();
            stopWatch.Stop();
        }
    }
    

另外提个小优化:你代码里的while (aTimer.Enabled)是同步阻塞线程的写法,建议改用异步等待或CancellationToken的方式实现超时逻辑,避免不必要的线程占用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 23:20:31