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

EF测试中InMemoryDatabase无法关联实体关系,结果不符预期

问题:InMemoryDatabase中Include关联失效导致测试返回0条数据

测试预期返回3条Entry数据,但实际结果为0。生产环境该功能正常,但在InMemoryDatabase中,Get方法的.Include(n => n.Task.ApplicationUser)语句无法正确关联Entry与Task,调试时Autos窗口显示两者已关联,推测是内存数据库配置或查询逻辑存在问题。

Get方法代码

public IEnumerable<Entry> Get(string userId, DateTime from, DateTime to)
{
     List<Entry> result;

     try
     {
         result = _db.Entries
             .Include(n => n.Task.ApplicationUser)   //InMemoryDb无法在此关联Task与Entries
             .Where(n => n.Task.ApplicationUserId == userId).Select(n => n)
             .Where(n => (n.Created.Date >= from.Date) && (n.Created.Date <= to.Date))
             .ToList();
     }
     catch (Exception e)
     {
         _logger.LogError($"Exception: {e.Message} StackTrace: {e.StackTrace}");
         return new List<Entry>();
     }
     return result;
};

内存数据库配置代码

private async Task<TasksContext> GetDbContext()
{
     var options = new DbContextOptionsBuilder<TasksContext>()
         .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
         .Options;

     // 创建模拟的DBcontext
     var dbContext = new TasksContext(options);
     dbContext.Database.EnsureDeleted();
     dbContext.Entries.AsNoTracking();

     dbContext.Tasks.Add(new Task { Id = 1, Name = "RunningId1", GroupId = 4, Created = _createdFirstEntry, ApplicationUserId = _userId });
     dbContext.Tasks.Add(new Task { Id = 2, Name = "CookingId2", GroupId = 4, Created = _createdSecondEntry, ApplicationUserId = _userId });
     dbContext.Tasks.Add(new Task { Id = 3, Name = "ReadingId3", GroupId = 4, Created = _createdThirdEntry, ApplicationUserId = _userId });

     dbContext.Entries.Add(new Entry { Id = _entryId_1, TaskId = _taskId_1, Duration = _duration, Created = _createdFirstEntry, LastUpdated = null, Deleted = null });
     dbContext.Entries.Add(new Entry { Id = _entryId_2, TaskId = _taskId_2, Duration = _duration, Created = _createdSecondEntry, LastUpdated = null, Deleted = null });
     dbContext.Entries.Add(new Entry { Id = _entryId_3, TaskId = _taskId_3, Duration = _duration, Created = _createdThirdEntry, LastUpdated = null, Deleted = null });
     await dbContext.SaveChangesAsync();
     
     return dbContext;
}

测试方法代码

public async void Get_TakesValidUserIdAndDateRange_ReturnsListWithThreeElements()
{
     // Arrange
     var dbContext = await GetDbContext();
     var entryRepository = new EntryRepository(dbContext, _logger, _mapper);

     // Act
     var result = entryRepository.Get(_userId,_createdFirstEntry, _createdThirdEntry);

     // Assert
     result.Should().BeOfType(typeof(List<Entry>));
     result.Count().Should().Be(3); 
}

解决方法

1. 移除无效的AsNoTracking()调用

dbContext.Entries.AsNoTracking()仅创建了一个无跟踪查询,并未对后续的实体添加操作生效,反而可能干扰实体关联,直接删除该行代码。

2. 确保外键值完全匹配

检查_taskId_1、_taskId_2、_taskId_3的取值是否分别为1、2、3(与Tasks的Id对应)。InMemoryDatabase不会自动关联外键,必须手动保证Entry的TaskId与Task的Id完全一致。

3. 调整查询逻辑顺序

先筛选Entry的日期范围,再进行关联查询,避免过早依赖未加载的关联实体,同时使用ThenInclude明确嵌套关联:

result = _db.Entries
    .Where(n => n.Created.Date >= from.Date && n.Created.Date <= to.Date)
    .Include(n => n.Task)
        .ThenInclude(t => t.ApplicationUser)
    .Where(n => n.Task.ApplicationUserId == userId)
    .ToList();

4. 修复测试方法的异步签名

async void会导致测试框架无法等待异步操作完成,将测试方法改为async Task:

public async Task Get_TakesValidUserIdAndDateRange_ReturnsListWithThreeElements()
{
     // Arrange
     var dbContext = await GetDbContext();
     var entryRepository = new EntryRepository(dbContext, _logger, _mapper);

     // Act
     var result = entryRepository.Get(_userId,_createdFirstEntry, _createdThirdEntry);

     // Assert
     result.Should().BeOfType(typeof(List<Entry>));
     result.Count().Should().Be(3); 
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 17:44:51