.NET集成测试中Singleton DbContextFactory被实例化两次的问题
问题原因分析
你遇到的核心问题是手动创建的临时服务提供者和应用实际使用的服务提供者不共享实例:
在ConfigureServices方法里,你调用了services.BuildServiceProvider(),这个方法会生成一个独立的临时服务容器。你用这个临时容器获取的DbContextFactory以及初始化的种子数据,都只存在于这个临时容器中。而WebHost启动时会重新构建一个全新的服务提供者(也就是应用实际运行依赖的容器),这两个容器是完全独立的——哪怕你注册的是Singleton,不同容器也会各自创建自己的Singleton实例,所以应用使用的DbContextFactory和你初始化数据用的根本不是同一个,自然读不到种子数据。
解决方案
移除手动构建服务提供者的代码,改用WebHost实际使用的服务容器来初始化数据,修改后的CustomWebApplicationFactory代码如下:
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { // 移除原有DbContext相关注册 removeService(services, typeof(DbContextOptions<PimsDbContext>)); removeService(services, typeof(IDbContextFactory<PimsDbContext>)); // 注册SQLite内存连接(单例) services.AddSingleton<DbConnection>(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); Console.WriteLine($"Connection address dbcontextfactory: {connection.GetHashCode()}"); return connection; }); // 注册单例DbContextFactory services.AddDbContextFactory<PimsDbContext>((container, options) => { var connection = container.GetRequiredService<DbConnection>(); options.UseSqlite(connection); }, ServiceLifetime.Singleton); services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>)); }); // 使用应用实际的服务容器初始化数据 builder.Configure(app => { using var scope = app.ApplicationServices.CreateScope(); var dbContextFactory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<PimsDbContext>>(); var db = dbContextFactory.CreateDbContext(); db.Database.EnsureCreated(); SeedData(db); }); } }
关键改动说明
builder.Configure方法中的app.ApplicationServices是WebHost启动后应用实际依赖的服务提供者,这里获取的DbContextFactory就是后续接口调用时会用到的单例实例。在这个容器里初始化的种子数据,会直接写入测试用的SQLite内存库,测试时接口就能正常读取到数据了。
同时请确保你的removeService方法能正确移除原项目中IDbContextFactory<PimsDbContext>的所有注册,避免出现多个实例冲突的情况。
内容的提问来源于stack exchange,提问作者ankushbbbr
相关产品推荐
相关产品推荐

