【问题标题】:Is it possible to rollback committed data with TransactionScope?是否可以使用 TransactionScope 回滚已提交的数据?
【发布时间】:2011-11-21 19:29:46
【问题描述】:

目标很简单——回滚单元测试插入的数据。这是怎么回事。在单元测试中,会调用一个创建新连接并插入一些数据的方法。之后,单元测试会创建一个新连接并尝试查找已插入的内容并对其进行断言。我希望用TransactionScope 包装这两件事,而不是调用Complete 并查看插入的数据回滚。那没有发生。是我做错了什么还是我没抓住重点?

using (new TransactionScope())
{
    // call a method that inserts data
    var target = new ....
    target.DoStuffAndEndupWithDataInDb();

    // Now assert what has been added.
    using (var conn = new SqlConnection(connectionString))
    using (var cmd = conn.CreateCommand())
    {
        // Just read the data from DB
        cmd.CommandText = "SELECT...";
        conn.Open();
        int count = 0;
        using (var rdr = cmd.ExecuteReader())
        {
            // Read records here
            ...
            count++;
        }

        // Expecting, say, 3 records here
        Assert.AreEqual(3, count);
    }
}

编辑:我认为我的机器上没有运行和配置 DTC。所以我启动了服务并尝试配置 DTC,但我收到了这个错误。

【问题讨论】:

  • 你的单元测试真的不应该触及任何像数据库这样的外部依赖。
  • 当然。考虑一个集成测试。顺便说一句,实际上我可能不使用 SQL 连接,而是使用内存数据库适配器。因此,尽管您的 cmets 通常是正确的,但它们对回答问题没有帮助。此外,这只是一个可以应用于不是单元测试而是程序中正常算法工作流的用例。
  • 您是否跟踪发送到数据库(SQL Profiler)的指令?
  • @Erno:没有。这是一个很好的建议。我想我不会在那里看到任何回滚。这就是为什么我想知道我是否做错了什么,或者这不是为那样工作而设计的。
  • 不,你没有错过重点。如果您不调用 Complete(),您绝对不应该看到提交到数据库的数据。 “DoStuff ...”方法内部实际发生了什么?此外,您是否真的需要为您的测试提供两个单独的连接对象,或者您可以在整个测试中使用单个 SqlConnection 和 SqlTransaction?

标签: c# .net sql transactionscope


【解决方案1】:

您在使用 MSTest 吗?那么你可以使用MsTestExtensions 你的单元测试需要从MSTestExtensionsTestFixture派生,你的测试需要有TestTransaction的属性,它使用AOP自动启动事务并回滚。

【讨论】:

    【解决方案2】:

    我认为您没有错过重点,只是错误地解决了问题。

    在 NUnit 术语中,概念是 [SetUp][TearDown] 方法。您已经在描述中定义了 setup 方法,并且您的 tear down 方法应该只是撤消 setup 方法所做的事情(假设您正在单元测试的内容没有残留的副作用)。

    【讨论】:

    • 嗯...可以做到。这在某种程度上消除了对事务的所有需求,因为那时我必须进行显式清理,我希望将其委托给回滚。
    【解决方案3】:

    您是否正确配置了分布式事务协调器?当尝试像这样使用TransactionScope 时,这是一个很大的问题...如果未配置,有时您会收到错误,但其他时候事务只会提交而不是回滚。

    I'd recommend looking at this article,它向您展示了使用 MSDTC 回滚单元测试所需完成的所有不同步骤。

    【讨论】:

    • 我尝试了您的建议,但未能在我的 Windows 7 机器上配置 DTC。该服务本身已关闭,因此我启动了该服务。但是,到目前为止,我无法更改组件服务管理单元中的属性(参见上图)。
    【解决方案4】:

    您的代码应该可以按预期工作。您如何在DoStuffAndEndupWithDataInDb() 中添加数据?我想知道数据初始化是否没有参与事务。

    作为参考,以下控制台应用程序正确输出3 rows,并且不会将行提交到数据库(使用 SSMS 检查)。

    public class Program
    {
        private static void Main(string[] args)
        {
            using (var trx = new TransactionScope())
            {
                InitializeData();
    
                using (var connection = new SqlConnection("server=localhost;database=Test;integrated security=true"))
                using (var command = connection.CreateCommand())
                {
                    command.CommandText = "select count(*) from MyTable";
                    connection.Open();
                    Console.WriteLine("{0} rows", command.ExecuteScalar());
                }
            }
            Console.ReadLine();
        }
    
        private static void InitializeData()
        {
            using (var connection = new SqlConnection("server=localhost;database=Test;integrated security=true"))
            using (var command = connection.CreateCommand())
            {
                command.CommandText = "insert into MyTable values (1),(2),(3)";
                connection.Open();
                command.ExecuteNonQuery();
            }
        }
    }
    

    【讨论】:

    • 事务范围内的多个连接仅在数据库和客户端上启用分布式事务管理器时才有效。也许将池设置为 true 并将池中的最大连接设置为 1 可能有效,但效率不高。
    • @Magnus,你的意思是分布式事务协调器服务吗?
    • @Magnus 我认为这里不需要 DTC,因为连接都到同一个数据库。我确实运行了 DTC,但停止了它,上面的代码仍然有效。
    • 根据 MSDN:“如果有多个连接,TransactionScope 类将事务视为分布式事务”。但也许这仅适用于连接重叠的情况,不确定。
    • @Magnus:DTC 没有在我的机器上启动,而是在 SQL 机器上。不幸的是,在我的机器上启动它没有任何区别......我有点沮丧,因为漂移说它适用于他的情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-20
    • 1970-01-01
    • 2020-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多