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

Testcontainers注册的DB上下文被Program类注册覆盖问题排查

问题分析与修复方案

核心问题

  1. 连接字符串生成时机错误:ConfigureWebHost在容器启动前就会执行,此时调用GetConnectionString()获取的映射端口尚未分配(Docker还未完成端口映射),导致注册的PersonContext使用了无效的连接字符串,后续即使容器启动,旧的DbContext配置依然生效。
  2. 服务移除不彻底:仅移除PersonContext服务,未移除对应的DbContextOptions<PersonContext>,原Program中注册的DbContext配置会通过Options继续生效。
  3. 服务配置顺序问题:InitializeAsync中获取的DbContext可能还是基于ConfigureWebHost阶段的无效配置创建的,没有使用容器启动后的正确连接字符串。

修复步骤

  1. 延迟DbContext配置到容器启动完成后,确保使用已分配的有效端口生成连接字符串。
  2. 彻底移除PersonContext及其对应的DbContextOptions<PersonContext>服务,清除旧配置。
  3. 在容器启动后重新注册DbContext并执行迁移,确保使用正确的连接字符串。

修复后的完整代码

public class CustomApiFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
    private readonly MsSqlContainer _msSqlContainer;

    public CustomApiFactory()
    {
        _msSqlContainer = new MsSqlBuilder()
           .WithPassword("mypass")
           .Build();
    }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestServices(services =>
        {
            // 彻底移除DbContext及对应的Options服务
            var optionsDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<PersonContext>));
            if (optionsDescriptor != null)
            {
                services.Remove(optionsDescriptor);
            }
            services.RemoveAll<PersonContext>();
            services.RemoveAll<DbContextOptions<PersonContext>>();

            // 注册MSSQL容器到服务,方便后续使用
            services.AddSingleton(_msSqlContainer);
        });
    }

    public async Task InitializeAsync()
    {
        await _msSqlContainer.StartAsync();
        var connectionString = GetConnectionString();

        using var scope = Services.CreateScope();
        // 重新注册使用正确连接字符串的DbContext
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDbContext<PersonContext>(opt => opt.UseSqlServer(connectionString));
        var serviceProvider = serviceCollection.BuildServiceProvider();
        
        var dbContext = serviceProvider.GetRequiredService<PersonContext>();
        // 可打印连接字符串验证是否正确
        // Console.WriteLine($"当前连接字符串:{dbContext.Database.GetDbConnection().ConnectionString}");
        await dbContext.Database.MigrateAsync();
    }

    public new async Task DisposeAsync() => await _msSqlContainer.DisposeAsync();

    private string GetConnectionString()
    {
        return $"Server=127.0.0.1,{_msSqlContainer.GetMappedPublicPort(1433)};Database=PersonDb;User Id=sa;Password=mypass;TrustServerCertificate=True";
    }
}

额外验证建议

在InitializeAsync中打印DbContext的连接字符串,确认是否指向Testcontainers启动的MSSQL容器。如果仍然指向旧数据库,检查:

  • Program中注册DbContext时是否使用了硬编码连接字符串或未被覆盖的配置源
  • 确保ConfigureTestServices中完全移除了所有与PersonContext相关的服务描述符

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 07:53:12