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

System.ObjectDisposedException求助:已释放EF上下文实例访问问题

解决System.ObjectDisposedException:已释放的DbContext实例访问问题

遇到System.ObjectDisposedException异常,错误信息:无法访问已释放的上下文实例。此错误的常见原因是使用依赖注入解析上下文实例后将其释放,之后又在应用程序其他地方尝试使用同一个上下文实例。

问题根源分析

  • JobbyContext作为EF Core的DbContext,默认是Scoped生命周期,它的生命周期与HTTP请求绑定,请求结束后会被DI容器自动释放。
  • TimerManager的定时器回调在后台异步执行,当触发回调时,原请求已经结束,注入到控制器的_Context实例早已被释放,此时再访问_Context.message就会抛出异常。
  • TimerManager中的Execute方法使用async void,会导致异步操作的异常无法被正确捕获和处理,属于不良实践。

解决方案

方案1:在定时器回调中创建新的DbContext实例

每次回调都生成全新的上下文实例,确保操作时上下文未被释放:

// 先在控制器中注入DbContext配置项
private readonly DbContextOptions<JobbyContext> _contextOptions;

public RealTimeMessage(IHubContext<MessageHub, IMessageHubClient> _messageHub, 
                       JobbyContext C, 
                       TimerManager T,
                       DbContextOptions<JobbyContext> contextOptions)
{
    messageHub = _messageHub;
    _Context = C;
    _timer = T;
    _contextOptions = contextOptions;
}

// 修改SendRealTimeMessage中的回调逻辑
[HttpGet]
[Route("SendMessage")]
public async Task<IActionResult> SendRealTimeMessage()
{
    if (!_timer.IsTimerStarted)
        await _timer.PrepareTimer(async ()=>
        {
            using var context = new JobbyContext(_contextOptions);
            var m = await context.message.ToListAsync(x=> x.IsRead==false);
            if (m.Count>0)
            {
                await messageHub.Clients.All.SendMessageToUser(m);
            }
        });
    
    return Ok(new { Message = "Request Completed" });
}

方案2:使用IServiceScopeFactory创建Scoped上下文

通过服务作用域工厂在回调中创建独立的作用域,从中获取DbContext:

// 在控制器中注入IServiceScopeFactory
private readonly IServiceScopeFactory _scopeFactory;

public RealTimeMessage(IHubContext<MessageHub, IMessageHubClient> _messageHub, 
                       JobbyContext C, 
                       TimerManager T,
                       IServiceScopeFactory scopeFactory)
{
    messageHub = _messageHub;
    _Context = C;
    _timer = T;
    _scopeFactory = scopeFactory;
}

// 修改回调逻辑
await _timer.PrepareTimer(async ()=>
{
    using var scope = _scopeFactory.CreateScope();
    var context = scope.ServiceProvider.GetRequiredService<JobbyContext>();
    var m = await context.message.ToListAsync(x=> x.IsRead==false);
    if (m.Count>0)
    {
        await messageHub.Clients.All.SendMessageToUser(m);
    }
});

额外修复:将async void改为async Task

修正TimerManager中的异步方法写法,避免异常丢失:

public class TimerManager
{
    private Timer? _timer;
    private AutoResetEvent? _autoResetEvent;
    private Func<Task>? _action;
    public DateTime TimerStarted { get; set; }
    public bool IsTimerStarted { get; set; }

    public void PrepareTimer(Func<Task> action)
    {
        _action = action;
        _autoResetEvent = new AutoResetEvent(false);
        _timer = new Timer(async (state) => await Execute(state), _autoResetEvent, 1000, 3000);
        TimerStarted = DateTime.Now;
        IsTimerStarted = true;
    }

    public async Task Execute(object? stateInfo)
    {
        await _action();

        if ((DateTime.Now - TimerStarted).TotalSeconds > 10000)
        {
            IsTimerStarted = false;
            _timer?.Dispose();
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 23:20:27