Когда я отлаживал следующий код, я обнаружил SqliteConnection, что я зарегистрировал время существования синглтона, а не синглтон, если разрешить его изнутри AddDbContext после того, как выполнение достигнет GetAsync("").
Я добавил следующее, чтобы убедиться, что синглтон не работает.
if (!ReferenceEquals(_sharedConnection, connection))
throw new ApplicationException("SqliteConnection should be singleton.");
Что является виновником и обходной путь? Полный код приведен ниже.
public class AppDbContext(DbContextOptions<AppDbContext> o) : DbContext(o);
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", (AppDbContext db) => "Hello");
app.Run();
}
}
public class CustomWebAppFactory : WebApplicationFactory<Program>
{
private static SqliteConnection _sharedConnection = null!;
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureTestServices(services =>
{
services.RemoveAll(typeof(SqliteConnection));
services.RemoveAll(typeof(DbContextOptions<AppDbContext>));
services.AddSingleton(provider =>
{
var connection = new SqliteConnection("DataSource=:memory:");
return connection;
});
services.AddDbContext<AppDbContext>((provider, options) =>
{
var connection = provider.GetRequiredService<SqliteConnection>();
if (!ReferenceEquals(_sharedConnection, connection))
throw new ApplicationException("SqliteConnection should be singleton.");
options.UseSqlite(connection);
});
var provider = services.BuildServiceProvider();
if (_sharedConnection != null)
throw new ApplicationException("_sharedConnection should be null before reaching the following line.");
_sharedConnection = provider.GetRequiredService<SqliteConnection>();
});
}
}
public class TrivialTest
{
[Fact]
public async Task Should_return_OK()
{
using var factory = new CustomWebAppFactory();
using var client = factory.CreateClient();
var response = await client.GetAsync("");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}





Области внедрения зависимостей применяются в одном контейнере IoC. Веб-сервер ASP.NET Core создает свой контейнер внутри себя, но вы создали отдельный контейнер, вручную вызвав .BuildServiceProvider() самостоятельно, и оба они имеют собственное одноэлементное соединение. Не делай этого.
Если ваше соединение должно пережить тест, не позволяйте контейнеру тестового сервера контролировать время жизни зависимости и создавайте его самостоятельно (именно поэтому другой ответ решает проблему, но объясняет, почему проблема возникает плохо)
Вывод на основе этого ответа:
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
// version 1
services.AddSingleton(new Foo());
// version 2
services.AddSingleton(isp => new Foo());
for (int i = 0; i < 2; ++i)
{
var providers = services.BuildServiceProvider();
var foo = providers.GetRequiredService<Foo>();
// prints the same GUID for version 1 but different GUIDs for version 2
foo.PrintId();
}
public class Foo
{
private readonly Guid _id = Guid.NewGuid();
public void PrintId() => Console.WriteLine(_id);
}