异步方法未完成时DBContext已被释放的问题排查
问题现象
仅在调用异步方法await context.SaveChangesAsync();时触发错误,改用同步方法context.SaveChanges()则运行正常。
错误信息
Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
相关代码
Program.cs
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddDbContext<ForumProjectContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("ConnectionString"))); builder.Services.AddScoped<ILoginService, LoginService>(); builder.Services.AddScoped<IRegisterService, RegisterService>(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Forum/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Forum}/{action=Index}/{id?}"); app.Run();
AccountController
using Microsoft.AspNetCore.Mvc; using Project_Fourm.Models; using Project_Fourm.Services; namespace Project_Fourm.Controllers { public class AccountController : Controller { private readonly ForumProjectContext ProjectContext; private readonly IRegisterService RegisterService; public AccountController(IRegisterService registerService, ForumProjectContext projectContext) { RegisterService = registerService; this.ProjectContext = projectContext; } [HttpGet] public IActionResult Login() { return View(); } [HttpPost] public IActionResult Login(LoginModel model) { if (ModelState.IsValid) { return RedirectToAction("Index", "Forum"); } else { return View(model); } } [HttpGet] public IActionResult Register() { return View(); } [HttpPost] public IActionResult Register(RegisterModel model) { if (ModelState.IsValid) { RegisterService.RegisterUser(ProjectContext, model.Username, model.Password, model.Email, model.Date); return RedirectToAction("Login"); } else { return View(model); } } } }
RegisterService
using Project_Fourm.Models; using System.CodeDom; namespace Project_Fourm.Services { public class RegisterService : IRegisterService { public async Task RegisterUser(ForumProjectContext context, string username, string password, string email, DateTime date) { User user = new User { Username = username, Passwd = password, Email = email, DateOfBirth = date, IsAdmin = false }; await context.Users.AddAsync(user); await context.SaveChangesAsync(); } } }
问题根源
控制器的Register方法是同步方法,调用异步的RegisterUser时未加await,导致控制器方法直接执行完毕并返回结果。此时DI容器会立即释放当前请求作用域内的DbContext实例,而后台的异步操作还在执行,当它尝试访问已释放的DbContext时就会触发错误。
解决步骤
1. 修正控制器的异步调用
将控制器的Register方法改为异步,并在调用RegisterUser时添加await:
[HttpPost] public async Task<IActionResult> Register(RegisterModel model) { if (ModelState.IsValid) { await RegisterService.RegisterUser(ProjectContext, model.Username, model.Password, model.Email, model.Date); return RedirectToAction("Login"); } else { return View(model); } }
2. (可选)优化依赖注入方式
不建议从控制器传递DbContext到服务,直接将DbContext注入到RegisterService中更符合DI设计原则:
- 修改RegisterService:
using Project_Fourm.Models; namespace Project_Fourm.Services { public class RegisterService : IRegisterService { private readonly ForumProjectContext _context; public RegisterService(ForumProjectContext context) { _context = context; } public async Task RegisterUser(string username, string password, string email, DateTime date) { User user = new User { Username = username, Passwd = password, Email = email, DateOfBirth = date, IsAdmin = false }; await _context.Users.AddAsync(user); await _context.SaveChangesAsync(); } } }
- 修改控制器中的调用:
await RegisterService.RegisterUser(model.Username, model.Password, model.Email, model.Date);
内容的提问来源于stack exchange,提问作者Anon2391

