【问题标题】:Refactoring ADO.NET - SqlTransaction vs. TransactionScope重构 ADO.NET - SqlTransaction 与 TransactionScope
【发布时间】:2009-08-13 09:29:53
【问题描述】:

我“继承”了一个小的 C# 方法,它创建一个 ADO.NET SqlCommand 对象并循环遍历要保存到数据库的项目列表 (SQL Server 2005)。

现在,使用传统的 SqlConnection/SqlCommand 方法,为了确保一切正常,这两个步骤(删除旧条目,然后插入新条目)被包装到 ADO.NET SqlTransaction 中。

using (SqlConnection _con = new SqlConnection(_connectionString))
{
   using (SqlTransaction _tran = _con.BeginTransaction())
   {
      try
      {
         SqlCommand _deleteOld = new SqlCommand(......., _con);
         _deleteOld.Transaction = _tran;
         _deleteOld.Parameters.AddWithValue("@ID", 5);

         _con.Open();

         _deleteOld.ExecuteNonQuery();

         SqlCommand _insertCmd = new SqlCommand(......, _con);
         _insertCmd.Transaction = _tran;

         // add parameters to _insertCmd

         foreach (Item item in listOfItem)
         {
            _insertCmd.ExecuteNonQuery();
         }

         _tran.Commit();
         _con.Close();
       }
       catch (Exception ex)
       {
          // log exception
          _tran.Rollback();
          throw;
       }
    }
}

现在,我最近阅读了很多关于 .NET TransactionScope 类的内容,我想知道,这里首选的方法是什么?通过切换到使用,我会获得什么(可读性、速度、可靠性)

using (TransactionScope _scope = new TransactionScope())
{
  using (SqlConnection _con = new SqlConnection(_connectionString))
  {
    ....
  }

  _scope.Complete();
}

你更喜欢什么,为什么?

马克

【问题讨论】:

    标签: sql-server ado.net transactions transactionscope


    【解决方案1】:

    通过将现有代码切换为使用TransactionScope,您不会立即获得任何收益。由于它提供的灵活性,您应该将其用于未来的开发。将来将 ADO.NET 调用以外的内容包含到事务中会更容易。

    顺便说一句,在您发布的示例中,SqlCommand 实例应该在 using 块中。

    【讨论】:

    • 好的,谢谢约翰 - 是的,你是对的 - 这个例子在 using() 块中没有 SqlCommand(还没有!) - 这是正在进行的工作:-)
    • re:SQLCommand里面使用,除了垃圾回收还有其他原因吗,例如:SQLConnection的Dispose()调用了Close()方法,那么Dispose()调用了任何SQLCommand方法吗?跨度>
    • 如果一个类实现了IDisposable,并且如果你创建了这个类的一个实例,那么你应该在它上面调用Dispose。最简单的方法是使用using 块。我发现最好养成始终实施的习惯。
    【解决方案2】:

    我更喜欢 TransactionScope。它并非在每种情况下都能完美运行,但在您描述的情况下,它是更好的解决方案。

    我的推理:

    1. 交易中的登记是自动的
    2. 发生异常时的事务回滚是自动的

    总而言之,结果是代码少了一点,设计通常更健壮,因为系统正在为我处理一些细节;这是我必须记住要做的一件事。

    此外,当您的 DAL 中有许多嵌套方法时,透明的事务注册会特别有用——尽管您必须注意不要意外地将事务变成需要 DTC 的分布式事务,这如果您使用多个 SqlConnections,即使它们指向同一个 DB,也会发生这种情况。

    【讨论】:

      【解决方案3】:

      微软建议使用事务范围:

      http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx

      基本思想是事务范围将为您管理“环境事务上下文”。您首先与一个数据库对话,您有一个 sql 事务,然后您与 2 号数据库对话,该事务被提升为分布式事务。

      事务范围确实适合您,因此您可以专注于系统的功能,而不是管道。

      编辑

      当您使用事务范围时,该范围内的所有内容都被事务覆盖。因此,您可以保存一行代码,将命令连接到事务。这可能是错误的来源,例如,如果在 1000 次中有一个机会忘记了这条线,那么您会丢失多少。

      编辑 2

      同意下面对 Triynko 的评论。但是,我们使用实体框架,EF 将自动关闭并重新打开连接以将其加入事务。它不像物理上关闭连接,而是将其释放到连接池并获得一个新的,可以是相同的,也可以是不同的。

      【讨论】:

      • 好的,。感谢那。但是,如果我将所有内容(不仅仅是这个示例)重构为使用 TransactionScope() 而不是 ADO.NET 嵌入式事务,我会以任何方式受益吗?只是问问努力是否值得——我从中得到了什么?
      • "当您使用事务范围时,该范围内的所有内容都被事务覆盖。"不,一切都没有涵盖。只有在作用域中登记的连接上发出的命令才会受作用域影响。如果在作用域中打开连接,则会自动在作用域中登记,否则在调用 SqlConnection.EnlistTransaction 创建后,需要手动将已打开的连接登记在作用域中。例如,如果您打开连接,然后创建事务范围......您的任何命令都不会参与事务。
      • @Triynko:您对相应 MSDN 文章的评论很棒!
      【解决方案4】:

      请注意使用 Transaction Scope 有时我们会遇到很多问题,因为我们必须在服务器中进行许多设置,例如设置 DTC、防火墙等。所以我建议使用 SqlTransaction 更节省实施。

      【讨论】:

        【解决方案5】:

        好吧,也许来不及了……不过无论如何,我会把它写下来给有兴趣的人……

        因为我现在有了更好的画面,在我目前基于SqlTransaction 的方法遇到很多困难之后,我可能会改用TransactionScope,正如我所看到的......TransactionScope 的主要优势是它可以很容易地在业务层中使用

        【讨论】:

          【解决方案6】:

          也迟到了……即使数据库不支持嵌套事务,您也可以轻松地在业务层中拥有“嵌套”事务。 .NET 控制嵌套并最终使用一个数据库事务(至少在 SQL Server 2008+ 的情况下)。这使得在其原始意图之外重用数据访问代码变得更加容易,作为更大事务的一部分。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多