C# EF DbContext报错:前序异步操作未完成即启动第二次操作
问题根因
这个错误完全是错误复用EF数据库上下文实例导致的:
- Entity Framework的
DbContext设计为单工作单元轻量对象,不支持线程安全,绝对不能跨请求、跨并发操作共享同一个实例,同一个实例上同时跑两个异步数据库操作就会触发你看到的报错。 - 你实现的
CentralContext.Create()用静态变量存储了全局唯一的CentralContext实例,所有接口、所有请求都复用这一个上下文:首次加载时没有并发操作,所以能正常运行;第二次重载、多接口同时调用时,前一个请求的异步数据库操作还没执行完成,后一个请求已经在同一个上下文实例上发起新操作,直接触发并发冲突。 - 你在Controller构造函数中初始化全局持有的
puntovService,进一步把全局单例的上下文绑定到了Controller生命周期,所有进入这个Controller的请求都用同一个上下文操作数据库,放大了冲突概率。
修复步骤
- 移除
CentralContext的单例逻辑
删掉静态变量和自定义的静态Create()方法,不要全局维护唯一上下文实例,建议同时关闭默认的延迟加载和代理生成,避免异步场景下的隐式数据库操作:using APICentralBL.Models; using System.Data.Entity; namespace APICentralBL.Data { public class CentralContext : DbContext { public CentralContext() : base("Context") { Configuration.ProxyCreationEnabled = false; Configuration.LazyLoadingEnabled = false; } public DbSet<SDN> SDN { get; set; } public DbSet<CALIF> CALIF { get; set; } public DbSet<UVT> UVT { get; set; } public DbSet<PUNTOV> PUNTOV { get; set; } public DbSet<TRANS> TRANS { get; set; } } } - 按请求创建上下文实例,用完即释放
DbContext应该遵循“每次请求/每次业务操作新建一个实例,操作完成立刻释放”的原则。如果你的项目没有接入依赖注入,最简单的改法是在接口方法内部创建上下文,不要在Controller构造函数里初始化持有上下文的服务:[HttpGet] [Route("Puntos")] [ResponseType(typeof(IEnumerable<PuntovDTO>))] public async Task<IHttpActionResult> GetAll() { using (var context = new CentralContext()) { var puntovService = new PuntovService(new PuntovRepository(context)); var pv = await puntovService.GetAll(); var puntovDTO = pv.Select(x => mapper.Map<PuntovDTO>(x)); var tipoComitePersona = from com in puntovDTO select new { com.PuntoV }; return Ok(tipoComitePersona); } } - (可选优化)接入依赖注入
后续可以引入依赖注入容器,把CentralContext、Repository、Service都注册为请求级生命周期,不用每次手动new对象,由容器自动管理实例创建和释放,代码更易维护。 - 常规检查
排查所有数据库异步操作,确保所有EF异步方法调用都使用await等待执行完成再发起下一个操作,你贴出的GetAll方法中await的写法没有问题,核心故障点就是全局单例的DbContext。
内容的提问来源于stack exchange,提问作者Hackerman
相关产品推荐
相关产品推荐

