重构ADO.NET - SqlTransaction vs. TransactionScope

33

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

现在,使用传统的 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类的文章,我想知道,在这种情况下,哪种方法更好?如果我改用XXX,我会获得什么优势(可读性、速度、可靠性)?

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

  _scope.Complete();
}

你更喜欢什么,为什么?

Marc

6个回答

19

如果你将现有代码切换到使用 TransactionScope,不会立即获得任何好处。你应该在未来的开发中使用它,因为它提供了灵活性。这将使将来包括除 ADO.NET 调用以外的其他内容进入事务更容易。

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


好的,谢谢约翰 - 是的,你说得对 - 这个例子还没有在using()块中使用SqlCommand(现在还在进行中) :-) - marc_s
关于在 using 语句块中使用 SQLCommand,除了垃圾回收之外,还有其他原因吗?例如:SQLConnection 的 Dispose() 方法会调用 Close() 方法,那么 Dispose() 方法是否会调用任何 SQLCommand 方法呢? - Troy DeMonbreun
4
如果一个类实现了 IDisposable 接口,并且当你创建该类的一个实例时,应该调用它的 Dispose 方法。最简单的方法是使用 using 块。我发现最好养成始终实现 using 块的习惯。 - John Saunders

10

我更喜欢使用TransactionScope。虽然它在每种情况下都不能完美地工作,但在您所描述的情况下,它是更好的解决方案。

我的理由:

  1. 自动加入事务
  2. 发生异常时自动回滚事务

综合起来,这意味着更少的代码和更加健壮的设计,因为系统会处理一些细节,这是我不必记住的一个 less thing。

此外,透明的事务登记在您的DAL中具有特别有用的作用--尽管您必须注意不要意外地将事务变成需要DTC的分布式事务,即使它们指向相同的DB,如果使用多个SqlConnection,则可能会发生这种情况。


8

微软建议使用事务范围:

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

基本思路是,事务范围将为您管理“环境事务上下文”。您首先连接一个数据库,有一个SQL事务,然后连接到第二个数据库,该事务被升级为分布式事务。

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

编辑

当您使用事务范围时,该范围内的所有内容都受事务覆盖。因此,您可以节省一行代码,其中将命令连接到事务。这是一个可能的错误源,例如如果有1000分之一的机会忘记了这一行,那么你会错过多少个呢。

编辑2

同意下面Triynko的评论。但是,我们使用Entity Framework,EF将自动关闭并重新打开连接以将其注册到事务中。它并不会物理地关闭连接,而是将其释放到连接池中并获取一个新连接,该连接可以是相同的连接也可以是不同的连接。


好的,谢谢你。但是,如果我将所有内容(不仅仅是这个示例)重构为使用TransactionScope()而不是ADO.NET嵌入式事务,我会从中受益吗?只是想问一下,这样做是否值得努力 - 我能获得什么好处? - marc_s
12
当您使用事务范围时,该范围内的所有内容都受事务覆盖。但并非所有内容都受到覆盖。仅当连接注册在范围内时发出的命令才会受到影响。如果在范围内打开连接,则会自动将其注册到范围中,否则需要通过调用SqlConnection.EnlistTransaction手动将已打开的连接注册到范围中。例如,如果您先打开连接,然后创建事务范围...则您的任何命令都不会参与事务。 - Triynko
1
@Triynko:你针对相应的MSDN文章的评论非常不错! - abatishchev

7

仅仅使用Transaction Scope有时会遇到很多问题,因为我们必须在服务器上进行许多设置,例如设置DTC、防火墙等等。因此,我建议在实现中使用SqlTransaction更加安全。


3

好的,也许现在已经太晚了……但无论如何,我会为那些有兴趣的人写下来……

由于我现在有了更清晰的认识,在我的当前基于 SqlTransaction 的方法遇到很多困难后,我可能会改用 TransactionScope。我认为,TransactionScope 的主要优点是在业务层中非常容易使用


2

同时晚了...即使数据库不支持嵌套事务,在业务层中也可以轻松地拥有“嵌套”事务。.NET控制嵌套,最终使用一个数据库事务(至少在SQL Server 2008+的情况下)。这样,就更容易重用数据访问代码,用作更大事务的一部分,而不仅限于原始意图。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接