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

单元测试中使用EnsureDeleted时如何不影响_EFMigrations表?

保留迁移表的数据库重置方案(.NET原生实现)

问题根源

EnsureDeleted()会直接删除整个数据库,包括EF Core用来追踪迁移记录的_EFMigrations表。后续执行数据库更新时,EF会误以为从未执行过任何迁移,试图从头运行所有迁移脚本,自然会和已存在的表结构冲突报错。

.NET原生替代方法

1. 只清空业务数据,保留库结构与迁移表

如果单元测试仅需要干净的测试数据,无需重建数据库结构,可以跳过EnsureDeleted(),用EnsureCreated()确保数据库存在后,手动清空所有业务表:

using var context = new YourDbContext();
// 确保数据库已创建(包含迁移表和完整结构)
context.Database.EnsureCreated();

// 获取所有业务表(排除迁移表)
var businessTables = context.Model.GetEntityTypes()
    .Where(entity => entity.GetTableName() != "_EFMigrations")
    .Select(entity => entity.GetTableName());

// 逐个清空表(PostgreSQL需处理外键和自增序列)
foreach (var table in businessTables)
{
    context.Database.ExecuteSqlRaw($"TRUNCATE TABLE \"{table}\" RESTART IDENTITY CASCADE;");
}
context.SaveChanges();

RESTART IDENTITY会重置自增字段的序列,CASCADE处理外键关联的级联清空,可根据业务需求调整

2. 备份迁移表后删库重建

如果必须重建数据库结构,但要保留迁移记录,可以先备份迁移表数据,删库重建后再恢复:

using var context = new YourDbContext();

// 备份迁移表数据
var migrationRecords = context.Database.SqlQueryRaw<MigrationHistory>(
    "SELECT MigrationId, ProductVersion FROM \"_EFMigrations\""
).ToList();

// 删除并重建数据库
context.Database.EnsureDeleted();
context.Database.EnsureCreated();

// 恢复迁移表记录
foreach (var record in migrationRecords)
{
    context.Database.ExecuteSqlRaw(
        "INSERT INTO \"_EFMigrations\" (MigrationId, ProductVersion) VALUES ({0}, {1})",
        record.MigrationId, record.ProductVersion
    );
}
context.SaveChanges();

注意:需确保MigrationHistory类的结构与_EFMigrations表字段匹配,也可直接用匿名类型接收数据

3. 用Migrate()替代EnsureCreated()

单元测试中直接调用Migrate(),它会自动应用所有未执行的迁移,不会删除任何表,之后再清空业务数据:

using var context = new YourDbContext();
// 应用所有未完成的迁移(保留迁移表状态)
context.Database.Migrate();

// 清空业务表(同方法1)
var businessTables = context.Model.GetEntityTypes()
    .Where(entity => entity.GetTableName() != "_EFMigrations")
    .Select(entity => entity.GetTableName());

foreach (var table in businessTables)
{
    context.Database.ExecuteSqlRaw($"TRUNCATE TABLE \"{table}\" RESTART IDENTITY CASCADE;");
}
context.SaveChanges();

这种方式和生产环境的迁移流程完全一致,避免EnsureCreated()可能导致的结构不一致问题,更适合长期维护

注意点

  • 尽量避免在单元测试中频繁调用EnsureDeleted(),除非完全不需要保留迁移状态
  • PostgreSQL表名区分大小写,SQL语句中要给表名加上双引号
  • 使用TRUNCATE前确认外键关系,避免因级联清空导致意外数据丢失

内容的提问来源于stack exchange,提问作者Роман Пермяков

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 14:01:14