【问题标题】:What is the best way to pass a SQL Server transaction as a parameter将 SQL Server 事务作为参数传递的最佳方法是什么
【发布时间】:2017-07-30 15:40:36
【问题描述】:

我知道在 .net C# 中使用“using”关键字和如下代码实现 SQL Server 事务的解决方案:

InsertDetails()
{
    using (TransactionScope ts = new TransactionScope()) 
    {    
        InsertName();//SQL functions to insert name into name table   
        Insertaddress();//SQL functions to insert address into address table
        InsertPhoneNo();//SQL functions to insert phone number into contact table

        ts.Complete();    
    }    
}

例如,我希望将 sql server 事务作为参数传递给不同数据库查询的许多不同函数,而不需要 using 语句示例。

在调用代码路径中的所有函数后,我想进行调用以提交数据,如果出现问题,则执行回滚。

伪代码如下所示

InsertDetails()
{
    var transaction = new Transaction();
    var sqlcon = new SqlConnection();    
        InsertName(transaction, sqlcon);//SQL functions to insert name into name table  
        Insertaddress(transaction, sqlcon);//SQL functions to insert address into address table
        InsertPhoneNo(transaction, sqlcon);//code to insert phone no into contact table
        try
        {
            ts.commit();       
        }
        catch(Exception ex)
        {
            ts.rollback();
        }
}

【问题讨论】:

  • 是什么阻止你这样做? (当然除了重新发明轮子)
  • 那么 using 语法更安全,将事务传递给方法的目的是什么?它应该是透明的

标签: c# sql-server transactions


【解决方案1】:

注意 #1:TransactionScope 可以升级为使用 MSDTC,因此使用 SqlTransaction 有助于避免这种行为。

注意 #2:默认情况下,TransactionScope 也将使用序列化隔离级别,当在行/表锁定中可能过于激进时。因此,您可能希望在使用 TransactionScope 时更改该行为。详情见:Why is System.Transactions TransactionScope default Isolationlevel Serializable

使用 SqlTransaction

坚持你的伪代码示例,我会改写成这样:

InsertDetails()
{
    using (var sqlcon = new SqlConnection(<connectionString>))
    {
        sqlcon.Open();

        // Create transaction to be used by all commands.
        var transaction = sqlcon.BeginTransaction();

        try
        {
            InsertName(transaction, sqlcon);//SQL functions to insert name into name table  
            Insertaddress(transaction, sqlcon);//SQL functions to insert address into address table
            InsertPhoneNo(transaction, sqlcon);//code to insert phone no into contact table

            transaction.commit();       
        }
        catch(Exception ex)
        {
            transaction.rollback();
            throw;
        }
    }
}

// Typical method implementation.
private void InsertName(SqlTransaction transaction, SqlConnection sqlcon)
{
    using (var cmd = sqlcon.CreateCommand())
    {
        // This adds this command to the transaction.
        cmd.Transaction = transaction;

        // The rest is fairly typical.
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandText = "InsertStoredProcedureName";
        ... set parameters etc.
        cmd.ExecuteNonQuery();
        ... handle any OUTPUT parameters etc.
    }
}

这将为任何被调用方法中的所有错误回滚事务。

【讨论】:

  • 不需要显式调用rollback,离开using语句不调用commit会导致rollback发生。如果您要在 catch 块中明确执行此操作,我将在回滚后添加 throw;,以便异常冒泡。
  • 你将如何使用事务对象进行 crud 查询?可以举个例子吗?
  • @Dizzle - 修改代码,显示方法中的示例 SqlTransaction 用法。每种方法都需要以类似的方式添加到事务中。
  • @ScottChamberlain - 感谢 cmets。我试图明确显示提交/回滚功能以进行澄清。我根据您的建议添加了throw; 作为更完整的示例。
  • 赞成投票,因为 TransactionScope 似乎有点矫枉过正,我用它来管理与多个 sql 服务器的连接(分布式事务)。
【解决方案2】:

我不推荐这种方法。

如果您是using new TransactionScope,那么您只需在一切正常时调用 Complete。如果没有,那么如果 Complete 没有被调用,那么一切都将被回滚。这有点违背了传递 Transaction 的目的,不是吗?我认为这种语法更清晰。

如果需要确定是否在事务上下文下运行,可以调用Transaction.Current获取当前事务。

【讨论】:

    【解决方案3】:

    以下模式适用于我的 DataContext(使用 Sql Server Profiler 确认)。您可能可以用 SqlConnection 替换 DataContext 。这不会导致将事务升级到 MSDTC。如果事务范围内出现异常或返回语句,事务将自动回滚。

    using (MyDataContext dc = new MyDataContext(MyConnectionString))
    {
        using (var t = new TransactionScope(TransactionScopeOption.Required,
            new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }))
        {
            InsertName(dc);
            InsertAddress(dc);
            InsertPhoneNo(dc);
            t.Complete();
        }
    }
    
    void InsertName(MyDataContext dc)
    {
       dc....
       dc.SubmitChanges();
    }
    

    【讨论】:

      【解决方案4】:

      我不确定,但我们可以简单地这样使用!还有其他问题吗?

      using (TransactionScope scope = new TransactionScope())
      {
        var isSuccess = Save();
        var isSuccess2 = Save2();
        if (isSuccess && isSuccess2 )
        {
          scope.Complete();
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-06-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-05
        相关资源
        最近更新 更多