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

异步执行双任务时DbContext抛出System.ObjectDisposedException异常

问题分析:DbContext重复使用导致ObjectDisposedException

问题描述

开发环境下构建邮件测试机制,邮件配置信息存储在SystemSettings数据库表中。该功能在Controller中可正常运行,但在Messenger.cs助手类调用SafeTestingSendEmail()方法时,抛出System.ObjectDisposedException异常,提示重复使用了同一个DbContext实例,调用_repoSystemSettings.GetSystemSettings(type)分别获取DEV_BCC_EMAIL和DEV_EMAIL对应的Value值时出错。

数据库表结构

IdNameValue
1DEV_BCC_EMAIL"myBcc@gmail.com,myPersonal@gmail.com"
2DEV_EMAIL"myPersonal@gmail.com"

相关代码

SystemSettingsRepo.cs

public class SystemSettingsRepo : ISystemSettingsRepo
{
    readonly DbContext _context;
    public SystemSettingsRepo(DbContext context) {
        _context = context;
    }

    public async Task<string> GetSystemSettings(string type = "")
    {
        var settings = await _context.SystemSettings.FirstOrDefaultAsync(s => s.IsActive == true && s.Name == type);
        return settings.Value;
    }
}

ISystemSettingsRepo.cs (接口)

public interface ISystemSettingsRepo
{
    Task<string> GetSystemSettings(string type = "");
}

Messenger.cs

private readonly ISystemSettingsRepo _repoSystemSettings;

public Messenger(ISystemSettingsRepo repoSystemSettings) {
  _repoSystemSettings = repoSystemSettings;
}

public async void SafeTestingSendEmail(string receiver, string subject, string body, string cc = "")
 {
    
     var env;
     var bcc = "defaultBcc@email.com";
     if (env.Length > 0 && env != "production")
     {

         var devBcc = await GetSystemSettingValue("DEV_BCC_EMAIL");
         var devEmail = await GetSystemSettingValue("DEV_EMAIL");

         receiver = devEmail;
         bcc = devBcc;
         cc = bcc;
         subject += "Testing Email.";

         SendEmail(receiver, subject, body, bcc, cc);
     }
     else
     {
         // production logic
     }
 }

 public async Task<string> GetSystemSettingValue(string type)
 {
     await Task.Delay(2000);
     return await _repoSystemSettings.GetSystemSettings(type);
 }

已尝试的解决方法

  • 尝试过Task.Delay()和Task.Run(),均无效。
  • 当前改用同步方式,通过List获取数据库记录解决问题,但放弃了异步实现,寻求更优方案。

问题根源与优化方案

问题根源

  1. async void的误用:SafeTestingSendEmail使用async void签名,这种写法仅适用于事件处理程序,普通异步方法使用它会导致异步操作脱离调用上下文,可能造成DbContext在异步操作完成前被提前释放。
  2. DbContext生命周期不匹配:如果Messenger是单例注入,而DbContext是Scoped生命周期,当Scoped结束后DbContext会被释放,后续调用就会触发ObjectDisposedException。
  3. 未处理空值:GetSystemSettings中如果FirstOrDefaultAsync返回null,直接访问settings.Value会抛出NullReferenceException。

优化方案

1. 修正异步方法签名

将async void改为async Task,确保异步操作能被正确等待,避免DbContext提前释放:

public async Task SafeTestingSendEmail(string receiver, string subject, string body, string cc = "")
{
    // 原有逻辑保持不变
}

调用该方法时需使用await等待异步完成。

2. 正确管理DbContext生命周期

如果Messenger是单例,避免直接注入Scoped的DbContext,改用IDbContextFactory创建DbContext实例,确保每次调用都使用新的上下文:

public class SystemSettingsRepo : ISystemSettingsRepo
{
    readonly IDbContextFactory<YourDbContext> _contextFactory;
    public SystemSettingsRepo(IDbContextFactory<YourDbContext> contextFactory) {
        _contextFactory = contextFactory;
    }

    public async Task<string> GetSystemSettings(string type = "")
    {
        using var context = _contextFactory.CreateDbContext();
        var settings = await context.SystemSettings.FirstOrDefaultAsync(s => s.IsActive == true && s.Name == type);
        return settings?.Value ?? string.Empty; // 添加空值判断
    }
}

3. 批量获取配置减少数据库访问

一次性查询所需的多个配置,减少DbContext的使用次数,同时提升性能:

// 在ISystemSettingsRepo中添加批量查询方法
Task<Dictionary<string, string>> GetSystemSettingsList(IEnumerable<string> types);

// SystemSettingsRepo中的实现
public async Task<Dictionary<string, string>> GetSystemSettingsList(IEnumerable<string> types)
{
    using var context = _contextFactory.CreateDbContext();
    var settings = await context.SystemSettings
        .Where(s => s.IsActive == true && types.Contains(s.Name))
        .ToDictionaryAsync(s => s.Name, s => s.Value);
    return settings;
}

// Messenger中的调用
var settings = await _repoSystemSettings.GetSystemSettingsList(new[] { "DEV_BCC_EMAIL", "DEV_EMAIL" });
var devBcc = settings.TryGetValue("DEV_BCC_EMAIL", out var bccVal) ? bccVal : "defaultBcc@email.com";
var devEmail = settings.TryGetValue("DEV_EMAIL", out var emailVal) ? emailVal : receiver;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 13:02:37