使用EF Core和内存Sqlite做单元测试时测试运行器出现栈溢出
排查内存Sqlite添加实体时的跨平台栈溢出问题
看起来你遇到了一个挺棘手的场景:在Ubuntu 20.04上用内存Sqlite做单元测试时,向DbContext添加实体直接触发栈溢出,但读取数据完全正常,而且在macOS上运行毫无问题。结合你的代码和描述,我整理了几个优先排查的方向:
1. 先检查BoardGameBuilder的实现逻辑
栈溢出最常见的根源就是无限递归,虽然你怀疑不是循环依赖,但Builder模式很容易出现这类低级错误:
- 检查Builder的
WithXxx方法是否不小心写成了递归调用自身,比如:
正确的链式调用写法应该是// 错误示例:返回了自身的方法调用,导致无限递归 public BoardGameBuilder WithName(string name) { this.Name = name; return WithName(name); }return this;。 - 另外检查
BoardGame实体类的构造函数、属性setter,有没有递归触发的逻辑(比如某个属性的setter会修改另一个属性,而后者又回调回来修改当前属性)。
2. 排查内存Sqlite的跨平台配置差异
既然问题只在Ubuntu上出现,大概率和Sqlite的运行时行为有关:
- 检查你的
TestDbFactory中内存Sqlite的连接字符串,是否在Ubuntu上缺少必要参数?比如是否添加了Cache=Shared?内存Sqlite在Linux上的默认缓存行为可能和macOS不同,多个DbContext实例共享连接时可能触发异常。 - 临时将内存Sqlite替换为临时文件Sqlite(比如连接字符串用
Data Source=./temp_test.db;Mode=Memory;Cache=Private),如果替换后问题消失,说明是内存Sqlite在Linux上的特定bug,此时可以考虑升级EF Core到最新稳定版,或者改用文件数据库做测试。 - 确认你使用的EF Core版本在Linux上有没有已知的内存Sqlite相关bug,比如旧版本的EF Core在处理某些实体映射时的递归问题。
3. 检查DbContext和实体的EF Core配置
- 查看
BoardGamesDbContext中是否重写了SaveChanges/SaveChangesAsync方法,里面有没有递归调用逻辑?比如调用SaveChanges时又触发了实体的变更,导致循环调用。 - 检查
BoardGame实体的导航属性配置,如果有双向导航(比如BoardGame关联Category,Category又关联回BoardGame),是否正确配置了EF Core的关系(比如用[InverseProperty]或者Fluent API)?虽然读取数据没问题,但添加实体时EF Core的变更追踪可能会遍历导航属性,在Linux运行时的栈限制下触发溢出。
4. 调试定位具体的调用栈
栈溢出时虽然很难拿到完整的调用栈,但可以尝试以下方法缩小范围:
- 在Ubuntu上用VS Code或其他调试器单步执行测试,观察添加实体前的最后几步调用,定位到递归的源头。
- 尝试逐个添加
TestBoardGames中的实体,看看是添加第一个就溢出,还是某个特定实体触发问题,这样可以锁定到具体实体的构造或配置问题。
5. 临时排除AutoMapper Mock的影响
虽然读取数据时Mock正常工作,但添加实体时EF Core的追踪逻辑可能间接触发Mapper的某些行为,你可以暂时简化Mock的Setup:
// 暂时替换为固定返回值,排除Mapper的影响 _mapperMock.Setup(mock => mock.Map<BoardGameDto>(It.IsAny<Models.BoardGame>())) .Returns(new BoardGameDto(0, "Test", 0, 0, 0));
如果替换后栈溢出消失,说明Mapper的Setup存在隐藏的递归问题。
内容的提问来源于stack exchange,提问作者Łukasz Sypniewski
相关产品推荐
相关产品推荐

