“ SqlTransaction已完成”错误的原因

时间:2019-06-08 04:43:58

标签: c# sql-server visual-studio transactions

我正在使用VS.net和SQL Server开发一个小型桌面应用程序。 我在代码中使用SqlTransaction

SqlTransaction tran = null;
SqlCommand cmd = new SqlCommand();
int lstInsSubjId = -1;

try
{
    using (SqlConnection con = new SqlConnection(connectionString))
    {
        con.Open();

        tran = con.BeginTransaction("Transaction1");//Transaction begin
        tran.Save("Savepoint_1");//Savepoint 1

        string sql1 = @"insSubject";
        cmd = new SqlCommand(sql1, con, tran);

        cmd.Parameters.Add("@lstInsSubjId", SqlDbType.Int).Direction = ParameterDirection.Output;
        cmd.Parameters.Add("@sub_name", SqlDbType.VarChar).Value = txtSubjectName.Text.Trim();

        cmd.CommandType = CommandType.StoredProcedure;
        tran.Save("Savepoint_2");//Savepoint 2

        cmd.ExecuteNonQuery();

        lstInsSubjId = Convert.ToInt32(cmd.Parameters["@lstInsSubjId"].Value);

        for (int i = 0; i < clbClasses.CheckedItems.Count; i++)
        {
            int clsId = Convert.ToInt32(((DataRowView)clbClasses.CheckedItems[i]).Row["c_id"].ToString());
            cmd.CommandText = @"INSERT INTO tblClassSubjectMap_mavis(c_id, sub_id) 
                                VALUES(@c_id, @sub_id)";
            cmd.Parameters.Add("@c_id", SqlDbType.Int).Value = clsId;
            cmd.Parameters.Add("@sub_id", SqlDbType.Int).Value = lstInsSubjId;

            cmd.CommandType = CommandType.Text;
            //tran.Save("Savepoint_3");//Savepoint 3

            cmd.ExecuteNonQuery();
        }

        tran.Commit();//Transaction commit
        MessageBox.Show("Records added", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);

        txtSubjectName.Text = "";
        txtSubjectName.Focus();
        frmSubjectBrows.subList.bindListView();
    }
}
catch (SqlException ex)
{
    if (tran != null)
    {
        tran.Rollback();
        MessageBox.Show(ex.Message.ToString() + "\nTransaction Rolledback", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
    }
}

在代码的结尾处,在for循环中,如果有多个检查项,即,如果它多次迭代/循环,则将引发错误,并且从不提交事务。但是,如果仅循环一次,则没有错误,事务将提交。

错误消息是:

  

在System.Data.dll中发生了类型为'System.InvalidOperationException'的未处理异常

     

其他信息:此SqlTransaction已完成;它不再可用。

在Internet上寻找答案时,一些解决方案指出,如果在提交之前突然关闭了事务,则可能会发生这种错误。但是我不明白交易在这里如何完成。

谁能告诉我出了什么问题?预先感谢。

1 个答案:

答案 0 :(得分:1)

为每个命令使用sqlcommand的新实例,请参见
上的示例 https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqltransaction?view=netframework-4.8

尝试此代码:

int lstInsSubjId = -1;

try
{
    using (SqlConnection con = new SqlConnection(connectionString))
    {
        con.Open();
        SqlCommand cmd = connection.CreateCommand();
        SqlTransaction tran = con.BeginTransaction("Transaction1");//Transaction begin
        cmd.Connection = cmd;
        cmd.Transaction = tran;

        tran.Save("Savepoint_1");//Savepoint 1

        cmd.CommandText = @"insSubject";
        cmd.Parameters.Add("@lstInsSubjId", SqlDbType.Int).Direction = ParameterDirection.Output;
        cmd.Parameters.Add("@sub_name", SqlDbType.VarChar).Value = txtSubjectName.Text.Trim();

        cmd.CommandType = CommandType.StoredProcedure;
        tran.Save("Savepoint_2");//Savepoint 2

        cmd.ExecuteNonQuery();

        lstInsSubjId = Convert.ToInt32(cmd.Parameters["@lstInsSubjId"].Value);

        for (int i = 0; i < clbClasses.CheckedItems.Count; i++)
        {
            int clsId = Convert.ToInt32(((DataRowView)clbClasses.CheckedItems[i]).Row["c_id"].ToString());
            SqlCommand cmd2 = connection.CreateCommand();
            cmd2.Connection = cmd2;
            cmd2.Transaction = tran;

            cmd2.CommandText = @"INSERT INTO tblClassSubjectMap_mavis(c_id, sub_id) 
                                VALUES(@c_id, @sub_id)";
            cmd2.Parameters.Add("@c_id", SqlDbType.Int).Value = clsId;
            cmd2.Parameters.Add("@sub_id", SqlDbType.Int).Value = lstInsSubjId;

            cmd2.CommandType = CommandType.Text;
            //tran.Save("Savepoint_3");//Savepoint 3

            cmd2.ExecuteNonQuery();
        }

        tran.Commit();//Transaction commit