将SQL Server事务作为参数传递的最佳方法是什么

时间:2017-07-30 15:40:36

标签: c# sql-server transactions

我知道使用“using”关键字在.net C#中实现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();
        }
}

3 个答案:

答案 0 :(得分:5)

注意#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.
    }
}

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

答案 1 :(得分:1)

我不推荐这种方法。

如果您是using new TransactionScope,那么只要一切都好,您就需要致电完成。如果没有,那么如果未调用Complete,则所有内容都将回滚。所以有点打败传递交易的目的,不是吗?在我看来,这种语法更清晰。

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

答案 2 :(得分:0)

以下模式对我来说适用于DataContext(已使用Sql Server Profiler确认)。您可能可以用SqlConnection代替DataContext。这不会导致将事务升级到MSDTC。如果交易范围内存在异常或return语句,则交易将自动回滚。

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();
}