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

ASP.NET Core集成测试中SQLite内存库上下文被释放问题排查

问题根源分析

你遇到的ObjectDisposedException核心原因非常明确:测试构造函数里的using var scope导致Scope在构造函数执行完毕后就被自动释放,而Scoped生命周期的AppDbContext会随着Scope的销毁被Dispose。当你在测试方法里调用_context.Customers.Add()时,这个上下文已经是被释放的状态,自然会报错。

另外你的CustomWebApplicationFactory还有一点小问题:没有确保SQLite内存数据库被正确初始化,这可能导致后续测试中数据库结构不存在的问题。

解决方案

方案1:调整测试类的Scope生命周期

把Scope保存为测试类的字段,避免在构造函数中提前释放,同时手动实现IDisposable接口来清理资源:

public class OrderControllerTests : IClassFixture<CustomWebApplicationFactory<Startup>>, IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly IServiceScope _scope; // 保存Scope,避免提前释放
    private readonly AppDbContext _context;

    public OrderControllerTests(CustomWebApplicationFactory<Startup> factory)
    {
        _httpClient = factory.CreateDefaultClient();
        var scopeFactory = factory.Services.GetRequiredService<IServiceScopeFactory>();
        _scope = scopeFactory.CreateScope(); // 去掉using,延长Scope生命周期
        _context = _scope.ServiceProvider.GetRequiredService<AppDbContext>();
    }

    [Fact]
    public async Task Create_Post_OrderIsCreated()
    {
        // 准备测试数据
        var customer = new Customer { /* 初始化字段 */ };
        _context.Customers.Add(customer);
        await _context.SaveChangesAsync();

        // 构造POST请求并发送
        var orderRequest = new OrderCreateRequest { CustomerId = customer.Id /* 其他字段 */ };
        var jsonContent = JsonContent.Create(orderRequest);
        var response = await _httpClient.PostAsync("/Order/Create", jsonContent);

        // 验证响应和数据库状态
        response.EnsureSuccessStatusCode();
        var createdOrder = await _context.Orders.FirstOrDefaultAsync(o => o.CustomerId == customer.Id);
        Assert.NotNull(createdOrder);
    }

    // 手动释放Scope和上下文
    public void Dispose()
    {
        _scope.Dispose();
    }
}

方案2:在测试方法内创建Scope(更推荐)

遵循依赖注入的最佳实践,在每个测试方法内部创建独立的Scope和DbContext,这样每个测试的资源都是隔离的,避免测试间的副作用:

public class OrderControllerTests : IClassFixture<CustomWebApplicationFactory<Startup>>
{
    private readonly HttpClient _httpClient;
    private readonly CustomWebApplicationFactory<Startup> _factory;

    public OrderControllerTests(CustomWebApplicationFactory<Startup> factory)
    {
        _httpClient = factory.CreateDefaultClient();
        _factory = factory;
    }

    [Fact]
    public async Task Create_Post_OrderIsCreated()
    {
        // 在测试方法内创建Scope和上下文
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        // 准备测试数据
        var customer = new Customer { /* 初始化字段 */ };
        context.Customers.Add(customer);
        await context.SaveChangesAsync();

        // 构造POST请求并发送
        var orderRequest = new OrderCreateRequest { CustomerId = customer.Id /* 其他字段 */ };
        var jsonContent = JsonContent.Create(orderRequest);
        var response = await _httpClient.PostAsync("/Order/Create", jsonContent);

        // 验证响应和数据库状态
        response.EnsureSuccessStatusCode();
        var createdOrder = await context.Orders.FirstOrDefaultAsync(o => o.CustomerId == customer.Id);
        Assert.NotNull(createdOrder);
    }
}

完善CustomWebApplicationFactory配置

确保SQLite内存数据库被正确初始化,并且Connection在整个测试周期内保持打开:

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
    private SqliteConnection _connection;

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        _connection = new SqliteConnection("DataSource=:memory:");
        _connection.Open();

        builder.UseEnvironment("Development");
        builder.ConfigureTestServices(services =>
        {
            // 移除原有的DbContext配置
            var descriptor = services.SingleOrDefault(
                d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
            if (descriptor != null)
                services.Remove(descriptor);

            // 注册SQLite内存数据库
            services.AddDbContext<AppDbContext>(options =>
                options.UseSqlite(_connection));

            // 初始化数据库结构
            var sp = services.BuildServiceProvider();
            using var scope = sp.CreateScope();
            var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
            context.Database.EnsureCreated();
        });
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        _connection?.Close();
        _connection?.Dispose();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:26:49