这是一个老问题,但很重要。我觉得很奇怪,我找不到任何关于此的文档。
无论如何,这是我的解决方案,我发现它会更好一些,因为您不需要依赖跑步者。由于您不需要为构造函数参数打开大量选项。
首先确保你安装了 Microsoft.Data.Sqlite 否则你会得到一个奇怪的错误。
只要连接存在,内存数据库中的 SQLite 就存在 - 乍一看,每个连接都有 1 个数据库。 实际上,根据我的实验,只要至少有一个连接始终处于打开状态,就有一种方法可以在连接之间共享数据库。你只需要命名它。
https://docs.microsoft.com/en-us/dotnet/standard/data/sqlite/connection-strings#sharable-in-memory
因此,我首先创建了一个连接,该连接将保持打开状态,直到测试完成。它将使用Guid.NewGuid() 命名,以便后续连接按预期工作。
var dbName = Guid.NewGuid().ToString();
var connectionString = $"Data Source={dbName};Mode=Memory;Cache=Shared";
var connection = new SqliteConnection(connectionString);
connection.Open();
之后运行迁移的关键与之前回答的相同,但连接字符串使用命名数据库:
var sp = services.AddFluentMigratorCore()
.ConfigureRunner(fluentMigratorBuilder => fluentMigratorBuilder
.AddSQLite()
.WithGlobalConnectionString(connectionString)
.ScanIn(AssemblyWithMigrations).For.Migrations()
)
.BuildServiceProvider();
var runner = sp.GetRequiredService<IMigrationRunner>();
runner.MigrateUp();
这是我用来在需要连接到数据库正常执行的任何地方注入连接工厂的类:
internal class PostgresConnectionFactory : IConnectionFactory
{
private readonly string connectionString;
public PostgresConnectionFactory(string connectionString)
{
this.connectionString = connectionString;
}
public DbConnection Create()
{
return new NpgsqlConnection(connectionString);
}
}
我只是将这个 (所有冰雹依赖倒置) 替换为:
internal class InMemoryConnectionFactory : IConnectionFactory
{
private readonly string connectionstring;
public InMemoryConnectionFactory(string connectionstring)
{
this.connectionstring = connectionstring;
}
public DbConnection Create()
{
return new SqliteConnection(connectionstring);
}
}
连接字符串与我上面定义的名称相同。
现在您可以在任何需要连接到相同内存数据库的地方简单地使用该连接工厂,并且因为我们现在可以连接多次进行集成测试。
这是我的大部分实现:
public static IDisposable CreateInMemoryDatabase(Assembly AssemblyWithMigrations, IServiceCollection services = null)
{
if (services == null)
services = new ServiceCollection();
var connectionString = GetSharedConnectionString();
var connection = GetPersistantConnection(connectionString);
MigrateDb(services, connectionString, AssemblyWithMigrations);
services.AddSingleton<IConnectionFactory>(new InMemoryConnectionFactory(connectionString));
return services.BuildServiceProvider()
.GetRequiredService<IDisposableUnderlyingQueryingTool>();
}
private static string GetSharedConnectionString()
{
var dbName = Guid.NewGuid().ToString();
return $"Data Source={dbName};Mode=Memory;Cache=Shared";
}
private static void MigrateDb(IServiceCollection services, string connectionString, Assembly assemblyWithMigrations)
{
var sp = services.AddFluentMigratorCore()
.ConfigureRunner(fluentMigratorBuilder => fluentMigratorBuilder
.AddSQLite()
.WithGlobalConnectionString(connectionString)
.ScanIn(assemblyWithMigrations).For.Migrations()
)
.BuildServiceProvider();
var runner = sp.GetRequiredService<IMigrationRunner>();
runner.MigrateUp();
}
private static IDbConnection GetPersistantConnection(string connectionString)
{
var connection = new SqliteConnection(connectionString);
connection.Open();
return connection;
}
然后这里是一个示例测试:
public Test : IDisposable {
private readonly IDisposable _holdingConnection;
public Test() {
_holdingConnection = CreateInMemoryDatabase(typeof(MyFirstMigration).Assembly);
}
public void Dispose() {
_holdingConnection.Dispose();
}
}
您可能会注意到静态工厂返回一个自定义接口。它只是一个接口,扩展了我注入到存储库的常规工具,但也实现了 IDisposable。
未测试的集成测试奖励,您将通过 WebApplicationFactory 或 TestServer 等创建服务集合:
public void AddInMemoryPostgres(Assembly AssemblyWithMigrations)
{
var lifetime = services.BuildServiceProvider().GetService<IHostApplicationLifetime>();
var holdingConnection= InMemoryDatabaseFactory.CreateInMemoryDapperTools(AssemblyWithMigrations, services);
lifetime.ApplicationStopping.Register(() => {
holdingConnection.Dispose();
});
}