运行XUnit测试类时出现DbUpdateConcurrencyException错误的修复方法
问题分析与解决方案
你遇到的问题核心是测试之间的隔离性不足:运行整个测试类时,XUnit默认会并行执行测试方法,或测试方法共享了数据库上下文/测试数据,导致DeleteWithCorrectDataTest执行时,目标记录已被其他测试提前删除。EF Core执行删除操作时检测到并发冲突(上下文预期删除1行,但数据库中无对应记录),从而抛出DbUpdateConcurrencyException。
修复方案:
1. 确保每个测试使用独立的数据库上下文
不要在测试类中共享同一个DbContext实例,每个测试方法都应创建全新的上下文。例如:
public class DeleteTests { private readonly AppDbContext _context; public DeleteTests() { // 每次生成唯一数据库名,确保测试数据完全隔离 var options = new DbContextOptionsBuilder<AppDbContext>() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; _context = new AppDbContext(options); } [Fact] public async Task DeleteWithCorrectDataTest() { // Arrange:单独插入当前测试需要的记录 var testWorkType = new WorkType { Id = 1, Name = "Test" }; _context.WorkTypes.Add(testWorkType); await _context.SaveChangesAsync(); var repository = new WorkTypeRepository(_context); // Act await repository.DeleteAsync(1); // Assert var deletedEntity = await _context.WorkTypes.FindAsync(1); Assert.Null(deletedEntity); } }
2. 为每个测试准备独立的测试数据
每个测试方法的Arrange步骤都要单独插入所需的测试数据,不要依赖全局初始化的共享数据,彻底避免不同测试操作同一条记录的情况。
3. 优化Delete方法的实现
如果你的Delete方法是通过Attach实体再删除的方式(如下),极易触发并发异常:
// 不推荐的实现 public async Task DeleteAsync(int id) { var entity = new WorkType { Id = id }; _context.Attach(entity); _context.Remove(entity); await _context.SaveChangesAsync(); }
改为先查询实体再删除,或使用ExecuteDeleteAsync直接执行SQL删除,避免上下文跟踪导致的并发检测:
// 推荐实现1:先查询再删除 public async Task<bool> DeleteAsync(int id) { var entity = await _context.WorkTypes.FindAsync(id); if (entity == null) return false; _context.Remove(entity); await _context.SaveChangesAsync(); return true; } // 推荐实现2:直接执行删除 public async Task<int> DeleteAsync(int id) { return await _context.WorkTypes .Where(w => w.Id == id) .ExecuteDeleteAsync(); }
测试时可通过断言返回值(true或1)验证删除是否成功。
4. 禁用测试并行执行(临时排查方案)
如果暂时无法完成测试隔离,可在测试项目的xunit.runner.json中禁用并行执行,确保测试类中的方法串行运行:
{ "parallelizeTestCollections": false }
注意:这只是临时排查手段,长期来看仍需做好测试隔离,避免测试间的依赖。
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

