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

如何在NUnit数据库测试中用Transaction Scope回滚多数据库连接

解决方案

方案1:为每个数据库单独管理本地事务(推荐用于测试数据清理)

.NET Core不支持依赖MSDTC的分布式TransactionScope,但可以给每个数据库的DbContext单独开启本地事务,测试完成后统一回滚,实现数据清理。这种方式不需要分布式事务,完全符合测试场景的需求。

代码示例

private DbContext1 _dbContext1;
private DbContext2 _dbContext2;
private DbContext3 _dbContext3;
private IDbContextTransaction _tran1;
private IDbContextTransaction _tran2;
private IDbContextTransaction _tran3;

[SetUp]
public void BeforeTest()
{
    // 初始化各数据库上下文并开启事务
    _dbContext1 = new DbContext1();
    _tran1 = _dbContext1.Database.BeginTransaction();

    _dbContext2 = new DbContext2();
    _tran2 = _dbContext2.Database.BeginTransaction();

    _dbContext3 = new DbContext3();
    _tran3 = _dbContext3.Database.BeginTransaction();
}

[TearDown]
public void AfterTest()
{
    // 按顺序回滚并释放资源
    _tran1.Rollback();
    _tran1.Dispose();
    _dbContext1.Dispose();

    _tran2.Rollback();
    _tran2.Dispose();
    _dbContext2.Dispose();

    _tran3.Rollback();
    _tran3.Dispose();
    _dbContext3.Dispose();
}

说明

  • 每个数据库的操作都在各自的本地事务中执行,测试中调用SaveChanges()只会将数据写入事务,不会提交到数据库。
  • TearDown阶段统一回滚所有事务,确保三个数据库的测试数据被彻底清理。
  • 这种方式不依赖分布式事务,完全适配.NET Core环境,同时.NET Framework 4.7项目也可以兼容使用。

方案2:测试前重置数据库(适合复杂场景)

如果测试逻辑涉及无法通过事务回滚的操作(比如触发了自动提交的存储过程、第三方数据同步),可以在SetUp阶段直接重置数据库:

  • 执行预定义的清理脚本,删除测试生成的数据。
  • 恢复测试专用的数据库备份到初始状态。

代码示例(执行清理脚本)

[SetUp]
public void BeforeTest()
{
    // 清理数据库1的测试数据
    using var db1 = new DbContext1();
    db1.Database.ExecuteSqlRaw("DELETE FROM TestTable1 WHERE IsTestData = 1");

    // 清理数据库2的测试数据
    using var db2 = new DbContext2();
    db2.Database.ExecuteSqlRaw("DELETE FROM TestTable2 WHERE IsTestData = 1");

    // 清理数据库3的测试数据
    using var db3 = new DbContext3();
    db3.Database.ExecuteSqlRaw("DELETE FROM TestTable3 WHERE IsTestData = 1");
}

说明

  • 需要给测试数据标记特殊标识(比如IsTestData字段),避免误删生产数据。
  • 相比事务回滚,这种方式性能稍差,但兼容性更强,适合所有.NET环境。

方案3:跨库操作原子性保障(若需事务一致性)

如果测试场景要求三个数据库的操作必须原子执行(要么全部成功,要么全部失败),可以实现简化版的补偿事务(Saga模式):

  1. 按顺序执行各数据库的操作,记录每个操作的状态。
  2. 若某一步失败,反向执行之前所有操作的回滚逻辑(比如删除已插入的数据、恢复修改前的状态)。

代码示例(简化版补偿逻辑)

[Test]
public void CrossDbOperationTest()
{
    bool db1Success = false;
    bool db2Success = false;

    try
    {
        // 操作数据库1
        using var db1 = new DbContext1();
        db1.TestEntities.Add(new TestEntity { IsTestData = true });
        db1.SaveChanges();
        db1Success = true;

        // 操作数据库2
        using var db2 = new DbContext2();
        db2.OtherEntities.Add(new OtherEntity { IsTestData = true });
        db2.SaveChanges();
        db2Success = true;

        // 操作数据库3并验证
        using var db3 = new DbContext3();
        db3.ValidationEntities.Add(new ValidationEntity { IsTestData = true });
        db3.SaveChanges();

        // 执行验证逻辑
        // ...
    }
    catch
    {
        // 补偿回滚
        if (db2Success)
        {
            using var db2 = new DbContext2();
            db2.Database.ExecuteSqlRaw("DELETE FROM OtherEntities WHERE IsTestData = 1");
        }
        if (db1Success)
        {
            using var db1 = new DbContext1();
            db1.Database.ExecuteSqlRaw("DELETE FROM TestEntities WHERE IsTestData = 1");
        }
        throw;
    }
    finally
    {
        // 清理所有测试数据
        using var db1 = new DbContext1();
        db1.Database.ExecuteSqlRaw("DELETE FROM TestEntities WHERE IsTestData = 1");
        using var db2 = new DbContext2();
        db2.Database.ExecuteSqlRaw("DELETE FROM OtherEntities WHERE IsTestData = 1");
        using var db3 = new DbContext3();
        db3.Database.ExecuteSqlRaw("DELETE FROM ValidationEntities WHERE IsTestData = 1");
    }
}

说明

  • 这种方式需要手动编写回滚逻辑,适合必须保证跨库操作一致性的测试场景。
  • 最终的finally块仍需执行统一清理,确保测试数据被彻底清除。

内容的提问来源于stack exchange,提问作者Vinny K

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 06:55:25