EF Core 内存数据库未保存到 DbSet

1ey*_*der 5 c# integration-testing unit-testing entity-framework-core asp.net-core

概述

我目前正在使用 EF Core 提供的内存数据库对我的存储库模式进行单元/集成测试。我使用的是 Microsoft.EntityFrameworkCore.InMemory nuget 版本 6.0.1,并使用 Visual Studio 2022。我在将数据保存到数据库时遇到问题。下面我包含了我的代码片段。

我的数据模型:

public class Example : IExample

public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
Run Code Online (Sandbox Code Playgroud)

我的基本存储库类:

public class Repository<T> : IRepository<T> where T : class

private readonly DbSet<T> _dbSet;
private readonly DbContext _db;

public Repository(DbContext db)
{
    _dbSet = db.Set<T>();
    _db = db;
}

public virtual async Task Add(T entity)
{
    await _dbSet.AddAsync(entity);
}

public virtual async Task<int> SaveAsync(CancellationToken token = default)
{
    return await _db.SaveChangesAsync(token);
}
Run Code Online (Sandbox Code Playgroud)

我的存储库类:

public class ExampleRepo : Repository<Example>

public ExampleRepo(ExampleContext db) : base(db)
{
}
Run Code Online (Sandbox Code Playgroud)

我的 DbContext 类

如果需要,我可以显示下面所示的ExampleBuilder的 IEntityConfiguration 类,但我不认为这是问题。

public class ExampleContext : DbContext

private readonly IHttpContextAccessor? _httpContextAccessor;

public ExampleContext(DbContextOptions<ExampleContext> options) : base(options)
{
    if (options == null) throw new ArgumentNullException(nameof(options));
}

public ExampleContext(DbContextOptions<ExampleContext> options, IHttpContextAccessor httpContextAccessor) 
    : base(options)
{
    if (options == null) throw new ArgumentNullException(nameof(options));
    _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}

public DbSet<Example>? Examples { get; set; }

protected override void OnModelCreating(ModelBuilder builder)
{
    var assembly = Assembly.GetAssembly(typeof(ExampleBuilder));
    if (assembly is null)
    {
        throw new DataException("Could not find the assembly containing the designated model builder");
    }

    builder.ApplyConfigurationsFromAssembly(assembly);

    foreach (var entity in builder.Model.GetEntityTypes())
    {
        entity.SetSchema("dbo");
    }
}

public override Task<int> SaveChangesAsync(CancellationToken token = default)
{
    foreach (var entry in ChangeTracker.Entries()
                                       .Where(x => x.State is EntityState.Added or EntityState.Modified or EntityState.Deleted))
    {
        var user = _httpContextAccessor?.HttpContext?.User?.Identity?.Name ?? "User";
        entry.Property("ModifiedBy").CurrentValue = user;

        if (entry.State == EntityState.Added)
        {
            entry.Property("CreatedBy").CurrentValue = user;
        }

        if (entry.State != EntityState.Deleted) continue;

        entry.State                              = EntityState.Modified;
        entry.Property("IsActive").CurrentValue = false;
    }

    return base.SaveChangesAsync(token);
}
Run Code Online (Sandbox Code Playgroud)

我的 DbContext 工厂方法:

private ExampleContext GenerateDbContext()
{
    var options = new DbContextOptionsBuilder<ExampleContext>()
                  .UseInMemoryDatabase(Guid.NewGuid().ToString())
                  .Options;

    return new ExampleContext(options);
}
Run Code Online (Sandbox Code Playgroud)

我使用 xUnit 和 NET6.0 进行单元/集成测试

[Fact]
public async Task GetAllEntities_ShouldReturnEntities_WhenEntitiesExist()
{
    // Arrange

    // I used Bogus nuget for single source for generating valid models
    var entity = ExampleFaker.GetModelFaker()
                             .Generate();

    await using var context = GenerateDbContext();
    var             repo    = new ExampleRepo(context);

    await repo.Add(entity);
    var changes = await repo.SaveAsync();

    // Act

    // Consulted this link already. Bottom answer is most related
    ///sf/ask/3232945621/
    var response = await repo.GetAll();

    // Assess
    TestOutputHelper.WriteLine($"Added entity to repository: {entity.ToJson()}");
    TestOutputHelper.WriteLine("Expected entities saved: 1");
    TestOutputHelper.WriteLine($"Actual entities saved: {changes}");
    TestOutputHelper.WriteLine($"Response: {response?.ToJson()}");

    // Assert
    Assert.Equal(1, changes);
    Assert.NotNull(response);
    Assert.NotEmpty(response);
    Assert.IsType<List<Example>>(response.ToList());
}
Run Code Online (Sandbox Code Playgroud)

分析与问题

Changes变量返回 1,因此我将其解释为 EF 对我的模型没有任何问题,并且我认为它成功地将我的模型保存在内存数据库中。但是,在我的GetAll检索过程中,没有返回任何数据。当我调试并查看存储库私有成员时,它显示 DbSet空,因此也不是GetAll方法导致问题。由于这也只是在单元测试的范围内,因此我认为我的 Program.cs 配置与我所看到的问题没有任何关系。我已经研究这个问题很长一段时间了,但无法弄清楚我一生中可能错过的小细节。

提前谢谢你的帮助。