【问题标题】:How to use SqlTransaction in C#如何在 C# 中使用 SqlTransaction
【发布时间】:2021-09-09 14:46:00
【问题描述】:

我正在使用以下代码一次执行两个命令。我使用 SqlTransaction 来确保所有命令都被执行或回滚。当我在没有“事务”的情况下运行我的程序时,它运行正常;但是当我对他们使用“交易”时,他们会显示错误。

我的代码:

SqlTransaction transaction = connectionsql.BeginTransaction();
            
try
{
    SqlCommand cmd1 = new SqlCommand("select account_name from master_account where NOT account_name = 'BANK' AND NOT account_name = 'LOAN'", connectionsql);
    SqlDataReader dr1 = cmd1.ExecuteReader();
    while (dr1.Read())
    {
        comboBox1.Items.Add(dr1[0].ToString().Trim());
    }
    cmd1.Dispose();
    dr1.Dispose();

    SqlCommand cmd2 = new SqlCommand("select items from rate",connectionsql);
    SqlDataReader dr2 = cmd2.ExecuteReader();
    while (dr2.Read())
    {
        comboBox2.Items.Add(dr2[0].ToString().Trim());
    }
    cmd2.Dispose();
    dr2.Dispose();
    transaction.Commit();

    dateTimePicker4.Value = dateTimePicker3.Value;
}
catch(Exception ex)
{
    transaction.Rollback();
    MessageBox.Show(ex.ToString());
}

错误:

【问题讨论】:

  • 为什么要在创建命令对象之前尝试执行事务?

标签: c# sql-server sqltransaction


【解决方案1】:

你必须告诉你的 SQLCommand 对象使用事务:

cmd1.Transaction = transaction;

或在构造函数中:

SqlCommand cmd1 = new SqlCommand("select...", connectionsql, transaction);

确保同时打开 connectionsql 对象。

但你所做的只是 SELECT 语句。当您使用 INSERT、UPDATE 等类型的操作时,事务会受益更多。

【讨论】:

    【解决方案2】:

    以下示例创建一个 SqlConnection 和一个 SqlTransaction。它还演示了如何使用 BeginTransaction、Commit 和 Rollback 方法。事务会在任何错误时回滚,或者如果它在没有首先提交的情况下被处置。 Try/Catch 错误处理用于在尝试提交或回滚事务时处理任何错误。

    private static void ExecuteSqlTransaction(string connectionString)
    {
        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            connection.Open();
    
            SqlCommand command = connection.CreateCommand();
            SqlTransaction transaction;
    
            // Start a local transaction.
            transaction = connection.BeginTransaction("SampleTransaction");
    
            // Must assign both transaction object and connection 
            // to Command object for a pending local transaction
            command.Connection = connection;
            command.Transaction = transaction;
    
            try
            {
                command.CommandText =
                    "Insert into Region (RegionID, RegionDescription) VALUES (100, 'Description')";
                command.ExecuteNonQuery();
                command.CommandText =
                    "Insert into Region (RegionID, RegionDescription) VALUES (101, 'Description')";
                command.ExecuteNonQuery();
    
                // Attempt to commit the transaction.
                transaction.Commit();
                Console.WriteLine("Both records are written to database.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Commit Exception Type: {0}", ex.GetType());
                Console.WriteLine("  Message: {0}", ex.Message);
    
                // Attempt to roll back the transaction. 
                try
                {
                    transaction.Rollback();
                }
                catch (Exception ex2)
                {
                    // This catch block will handle any errors that may have occurred 
                    // on the server that would cause the rollback to fail, such as 
                    // a closed connection.
                    Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
                    Console.WriteLine("  Message: {0}", ex2.Message);
                }
            }
        }
    }
    

    SqlTransaction Class

    【讨论】:

    • 但是CreateCommand 返回一个与连接关联的sqlcommand。那么为什么要设置sqlcommand的connection prop呢?
    【解决方案3】:

    您可以从SqlConnection 创建一个SqlTransaction

    并用它创建任意数量的SqlCommands

    SqlTransaction transaction = connection.BeginTransaction();
    var cmd1 = new SqlCommand(command1Text, connection, transaction);
    var cmd2 = new SqlCommand(command2Text, connection, transaction);
    

    或者

    var cmd1 = new SqlCommand(command1Text, connection, connection.BeginTransaction());
    var cmd2 = new SqlCommand(command2Text, connection, cmd1.Transaction);
    

    如果命令失败从不导致意外更改不要使用事务。

    如果命令失败可能导致意外更改,请将它们放在 Try/Catch 块中,并在 另一个 Try/Catch 块中回滚操作。

    为什么还要尝试/捕获?根据MSDN

    Try/Catch 异常处理应始终在回滚事务时使用。如果连接终止或事务已在服务器上回滚,则回滚会生成 InvalidOperationException

    这是一个示例代码:

    string connStr = "[connection string]";
    string cmdTxt = "[t-sql command text]";
    
    using (var conn = new SqlConnection(connStr))
    {
        conn.Open();
        var cmd = new SqlCommand(cmdTxt, conn, conn.BeginTransaction());
        
    
        try
        {
            cmd.ExecuteNonQuery();
            //before this line, nothing has happened yet
            cmd.Transaction.Commit();
        }
        catch(System.Exception ex)
        {
            //You should always use a Try/Catch for transaction's rollback
            try
            {
                cmd.Transaction.Rollback();
            }
            catch(System.Exception ex2)
            {
                throw ex2;
            }
            throw ex;
        }
    
        conn.Close();
    }
    

    如果在调用 Commit 或 Rollback 之前 处理事务,则该事务被 回滚

    因此您无需担心应用被关闭。

    【讨论】:

    • 为什么我们总是要使用 try/catch 块来回滚事务?
    • @ChristopherBerman 原因包含在答案中,并附有 MSDN 文章的链接
    • 啊,我不知道您正在通过 MSDN 的建议(略读太快)。从 MSDN 文章中我不清楚为什么我们必须这样做,除了记录最佳实践。
    【解决方案4】:

    好吧,我不明白你为什么在你做select时使用事务。

    当您从数据库中更改(添加、编辑或删除)数据时,事务非常有用。

    删除事务,除非您使用 insertupdatedelete 语句

    【讨论】:

    • 我认为这只是一个例子
    • 我现在正在做一个项目,在这个项目中,围绕 Select 进行交易是有意义的。正在进行更新查询,事务在连接上的更高级别启动。为了确保所有服务器数据都处于更新的有效状态,我需要进行几次读取。如果 SqlCommand 上没有事务,则会出现此错误:“当分配给命令的连接处于挂起的本地事务中时,ExecuteReader 要求该命令具有事务。”
    【解决方案5】:

    使用 sql 事务更新或删除

     private void SQLTransaction() {
       try {
         string sConnectionString = "My Connection String";
         string query = "UPDATE [dbo].[MyTable] SET ColumnName = '{0}' WHERE ID = {1}";
    
         SqlConnection connection = new SqlConnection(sConnectionString);
         SqlCommand command = connection.CreateCommand();
         connection.Open();
         SqlTransaction transaction = connection.BeginTransaction("");
         command.Transaction = transaction;
         try {
           foreach(DataRow row in dt_MyData.Rows) {
             command.CommandText = string.Format(query, row["ColumnName"].ToString(), row["ID"].ToString());
             command.ExecuteNonQuery();
           }
           transaction.Commit();
         } catch (Exception ex) {
           transaction.Rollback();
           MessageBox.Show(ex.Message, "Error");
         }
       } catch (Exception ex) {
         MessageBox.Show("Problem connect to database.", "Error");
       }
     }
    

    【讨论】:

      【解决方案6】:

      首先,您不需要事务,因为您只是查询 select 语句,而且它们都是 select 语句,您可以将它们组合成一个用空格分隔的查询,并使用 Dataset 来获取检索到的所有表。这种方式更好,因为您只对数据库进行了一次事务,因为数据库事务很昂贵,因此您的代码更快。 第二个你真的要使用事务,只需将事务分配给 SqlCommand 之类的

      sqlCommand.Transaction = transaction;
      

      而且只使用一个 SqlCommand 不要声明多个,因为变量会占用空间,而且我们还致力于让您的代码更高效,通过将 commandText 分配给不同的查询字符串并像执行它们一样来做到这一点

      sqlCommand.CommandText = "select * from table1";
      sqlCommand.ExecuteNonQuery();
      sqlCommand.CommandText = "select * from table2";
      sqlCommand.ExecuteNonQuery();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多