如何转换为ADO.NET事务而不是SQL Server事务?

时间:2010-02-01 17:07:20

标签: sql-server ado.net transactions

现在我有了使用预期方法在SQL Server上启动事务的代码:

ExecuteNonQuery(connection, "BEGIN TRANSACTION");
try
{
   DoABunchOnStuff(connection);
   DoSomeMoreStuff(connection);
   JustAFewMoreThings(connection);

   ExecuteNonQuery(connection, "COMMIT TRANSACTION");
} 
catch (Exception)
{  
   ExecuteNonQuery(connection, "ROLLBACK TRANSACTION");
   throw;
}

现在我正在考虑调查使用ADO.NET提供的事务抽象的想法的可能性:

DbTransaction trans = connection.BeginTransaction();
try
{
   DoABunchOnStuff(connection);
   DoSomeMoreStuff(connection);
   JustAFewMoreThings(connection);

   trans.Commit();
} 
catch (Exception)
{  
   trans.Rollback();
   throw;
}

从基于SQL Server的事务到ADO.NET事务的这种简单转换的问题是错误:

  

ExecuteNonQuery需要该命令   当有一个交易时   分配给命令的连接是   在待处理的本地事务中。该   命令的Transaction属性   尚未初始化。

我是否正确假设如果我想使用ADO.NET事务,我将不得不完全消除基础设施,将 DbTransaction 对象传递给在内部或可能在内部运行的每个方法交易?

3 个答案:

答案 0 :(得分:5)

你是对的,但由于你显然一直保持连接打开,你可以用TransactionScope代替它;只要只有一个开放连接,它就不会升级到DTC。

示例:

using (TransactionScope tsc = new TransactionScope())
{
    DoABunchOnStuff(connection);
    DoSomeMoreStuff(connection);
    JustAFewMoreThings(connection);
    tsc.Complete();
}

关于使用TransactionScope

的说明
  • 您必须确保在连接字符串中包含Transaction Binding = Explicit Unbind。默认情况下,事务以implicit-unbind模式运行,这意味着如果事务超时,它们将切换到自动提交模式。您几乎从不想要默认行为,因为它可能会干扰您的事务的原子性并导致某些人称之为数据损坏(即使它不是真正的“损坏”)。只要在连接字符串中使用正确的参数,就不必担心这一点。

  • 如果范围内有多个连接,则
  • TransactionScope将提升为DTC(分布式事务),其中包括链接服务器和OPENROWSET。虽然这可能看起来像是不受欢迎的行为,但您的代码在任何其他方面都不会是事务安全的。在多个连接上执行手动BEGIN TRAN语句并在异常处理程序中放置多个ROLLBACK语句确保整个事务的原子性。

  • 交易范围旨在嵌套,并自动计算出开始新交易和参与现有交易之间的区别。这比匹配BEGIN TRANCOMMIT / ROLLBACK语句要强大得多,因为后者依赖于连接本地事务计数,而前者实际上是...... 作用域。使用TransactionScope类似于SQL Server中使用SAVE TRANTRY / CATCH和名为ROLLBACK的结构化事务处理 - 您无需担心什么如果下游流程或过程淹没事务逻辑,则会发生这种情况,这在通过ADO.NET发送原始BEGINROLLBACK语句时存在严重风险。

答案 1 :(得分:3)

  

我认为如果我是正确的   想用ADO.NET交易我   必须完全消除   基础设施,传递   每个方法的DbTransaction对象   那可以或者可以在一个内部运作   交易?

是的,确切地说 - 你基本上需要将你创建的事务与应该在该事务的保护伞下执行的每个SqlCommand相关联 - 所以你必须有类似的东西:

DbTransaction trans = connection.BeginTransaction();
try
{
   DoABunchOnStuff(connection, trans);
   DoSomeMoreStuff(connection, trans);
   JustAFewMoreThings(connection, trans);

   trans.Commit();
} 
catch (Exception)
{  
   trans.Rollback();
   throw;
}

并且在这些方法中包含以下内容:

public void DoABunchOnStuff(SqlConnection connection, SqlTransaction trans)
{
    using(SqlCommand cmd = new SqlCommand(--sql stmt--, connection, trans)
    {
       ........
    } 
} 

答案 2 :(得分:-1)

您可能还想看看Linq to SQL。您还可以在代码中将“SubmitChanges()”(或不提交它们)发送到数据库。这意味着您可以将其包装在try catch中,就像您的事务一样。这也是一个基础设施变化,但是SQLMetal你可以自动生成所有必要的类。它可能适合您的情况,也可能不适合。

更多信息:http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx