【问题标题】:How to convert to ADO.NET transactions rather than SQL Server Transactions?如何转换为 ADO.NET 事务而不是 SQL Server 事务?
【发布时间】:2010-02-01 17:07:20
【问题描述】:

现在我有使用预期方法在 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 需要命令 进行交易时 分配给命令的连接是 在待处理的本地事务中。这 命令的事务属性 尚未初始化。

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

【问题讨论】:

    标签: sql-server ado.net transactions


    【解决方案1】:

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

    例子:

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

    关于使用TransactionScope的注意事项:

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

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

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

    【讨论】:

    • TransactionScope 看起来很有趣,但文档甚至无法确定正确的语法。并且有一些 cmets 在默认模式下谈论可能的数据损坏。我想我会坚持尝试和真实的交易。
    • @Ian Boyd:你到底在说什么?我从未听说过由TransactionScope 引起的数据损坏。更重要的是,您可以嵌套它们,这是常规事务无法做到的。方式,方式更可靠,更容易维护你现在所拥有的。诚然,MSDN 上的代码示例很糟糕,但该库是坚如磐石的。
    • @Aaronaught:我只是告诉你 Florin Lazar - MSFTTransactionScope 的文档页面上所说的话。单击答案中的链接。他说,“在将 TransactionScope 与 System.Data.SQLClient 一起使用时,必须在连接字符串中指定“Transaction Binding = Explicit Unbind”,以确保范围内的原子性并防止数据不一致或损坏。” Don不要向信使开枪 - 与警告可能存在数据损坏的 Microsoft 人员交谈。
    • @Ian Boyd:这是一个很容易解决的老错误。只要记住把它放在你的连接字符串中就可以了。
    • @Aaronaught。它可能是固定的,或者很容易解决。但是由于我正在处理与原子事务完整性一样重要的事情,并且我在其他两种数据库访问技术(BDE 和 ADO)提供的事务抽象方面有过糟糕的经历,所以我对使用它感到很害羞。一朝被蛇咬十年怕井绳。被咬过两次,害羞过八次。然后我发生阅读此事务模型的默认使用模式如何导致数据损坏。被咬了三次……
    【解决方案2】:

    我是否正确假设如果我 我想使用 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)
        {
           ........
        } 
    } 
    

    【讨论】:

    • 这确实是一个奇怪的结构。除了连接之外,事务可以在什么情况下进行。 (至少在 sql server 中)
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-21
      • 1970-01-01
      • 2011-02-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多