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

ASP.NET Core MVC二次请求触发DbContext并发操作异常排查

问题:ASP.NET Core MVC中DbContext并发操作异常

场景复现

开发ASP.NET Core MVC应用时,前端通过AJAX发送POST请求切换广告商激活状态,首次请求正常,第二次请求抛出以下异常:

System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext.

重启项目后可再次正常执行一次请求,第二次仍报错。已检查代码中异步操作均使用await,但问题依旧。

相关代码

前端AJAX请求

$.ajax({
    url:'/Advertisers/ActiveAdvertiser?id='+id+'&isActive='+!isActive,
    method: 'POST',
    success: function(r){
        Swal.fire("Inactivated!", "Advertiser inactivated successfully", "success");
    },
    error: function (request) {
        console.log(request.responseText)
        Swal.fire("Error!", "Something went wrong, please try again`", "warning");
    }
});

控制器代码

[HttpPost]
public async Task<JsonResult> ActiveAdvertiser(int id, bool isActive)
{
    var advertiser = await _advertisersService.GetAdvertiserByAdvertiserIdAsync(id);

    if (advertiser != null)
    {
        var model = AssingAdvertiserViewModel(advertiser, id);
        model.IsActive = isActive;

        var result = await _advertisersService.UpdateAdvertiserAsync(model, GetCurrentUserAsync().Id);

        if (result != null)
        {
            return Json(new { result = "OK" });
        }
    }

    return Json(new { result = "BadRequest" });
}

服务层代码

public Task<Advertiser?> GetAdvertiserByAdvertiserIdAsync(int advertiserId)
{
    return _db.Advertisers
              .Include(a => a.Address)
              .Include(pc => pc.PrimaryContact)
              .Include(ac => ac.AlternateContact)
              .FirstOrDefaultAsync(x => x.AdvertiserId == advertiserId);
}

private AdvertiserViewModel AssingAdvertiserViewModel(Advertiser advertiser, int id)
{
    var model = new AdvertiserViewModel()
    {
        //Fill model here
    };

    return model;
}

public async Task<Advertiser?> UpdateAdvertiserAsync(AdvertiserViewModel model, int updatedById)
{
    var advertiser = await GetAdvertiserByAdvertiserIdAsync(model.AdvertiserId);

    if (advertiser is null)
        return null;

    advertiser.Name = model.Name;

    // fill model here

    await _db.SaveChangesAsync();

    return advertiser;
}

问题排查与解决方案

核心原因

异常本质是DbContext实例被多个请求并发复用。ASP.NET Core中DbContext默认应为Scoped生命周期(每个请求创建一个实例),若DbContext或服务层类被注册为Singleton,会导致所有请求共用同一个DbContext实例,触发并发操作冲突。

具体排查与修复点

  1. 检查DbContext注册方式
    确保在Program.cs(或Startup.cs)中DbContext按Scoped注册,正确写法:

    builder.Services.AddDbContext<YourDbContext>(options => 
        options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
    

    (AddDbContext默认即为Scoped生命周期)

  2. 检查服务层类注册方式
    若AdvertisersService被注册为Singleton(如使用AddSingleton),会长期持有同一个DbContext实例。需改为Scoped或Transient:

    builder.Services.AddScoped<IAdvertisersService, AdvertisersService>();
    
  3. 修正GetCurrentUserAsync()调用
    控制器中GetCurrentUserAsync().Id存在异步操作未完成就访问结果的问题,需改为:

    (await GetCurrentUserAsync()).Id
    

    避免获取到Task对象的Id而非实际用户Id,同时保证异步流程完整。

验证

修改注册方式与代码后,重启应用,多次发送请求即可解决DbContext并发操作异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 17:55:15