iT邦幫忙

0

C# already an open DataReader

  • 分享至 

  • xImage

Hi各位大老,下列問題我實際上已經用別的寫法解決,
但我還是想問一下關於下列程式碼是哪裡出問題?
我看我設置的reader都有關閉,但是卻一直跳錯誤。
都是在updateStageCommand.ExecuteNonQuery();這條出現(X)
訊息:
System.InvalidOperationException: 'There is already an open DataReader associated with this Connection which must be closed first.'
程式要達成的目標是:
當我刪除任一筆資料時,如果同個uid中有stage
大於要刪除的stage,那麼大於的部分都要-1
資料修正前(移除a2):
rid:a1、uid:ariel、stage:1
rid:a2、uid:ariel、stage:2
rid:a3、uid:ariel、stage:3
rid:a4、uid:ariel、stage:4
資料修正後:
rid:a1、uid:ariel、stage:1
rid:a3、uid:ariel、stage:2
rid:a4、uid:ariel、stage:3

我有兩種寫法,一開始只有用前端抓取rid。然後到資料庫操作。

public void DeletePoSignById(string rid)
        {
            SqlConnection sqlConnection = new SqlConnection(ConnStr);
            SqlCommand sqlCommand = new SqlCommand("SELECT uid, stage FROM Po_sign WHERE rid = @rid");
            sqlCommand.Connection = sqlConnection;
            sqlCommand.Parameters.Add(new SqlParameter("@rid", rid));
            sqlConnection.Open();
            SqlDataReader reader = sqlCommand.ExecuteReader();
            string uid = "";
            int stage = 0;
            if (reader.Read())
            {
                uid = Convert.ToString(reader["uid"]);
                stage = Convert.ToInt32(reader["stage"]);
            }
            reader.Close();

            sqlCommand = new SqlCommand("SELECT * FROM Po_sign WHERE uid = @uid");
            sqlCommand.Connection = sqlConnection;
            sqlCommand.Parameters.Add(new SqlParameter("@uid", uid));
            SqlDataReader relatedReader = sqlCommand.ExecuteReader();
            bool hasHigherStage = false;
            while (relatedReader.Read())
            {
                int relatedStage = Convert.ToInt32(relatedReader["stage"]);
                if (relatedStage > stage)
                {
                    hasHigherStage = true;
                    int updatedStage = relatedStage - 1;
                    SqlCommand updateStageCommand = new SqlCommand("UPDATE Po_sign SET stage = @updatedStage WHERE uid = @uid AND stage = @relatedStage");
                    updateStageCommand.Connection = sqlConnection;
                    updateStageCommand.Parameters.Add(new SqlParameter("@updatedStage", updatedStage));
                    updateStageCommand.Parameters.Add(new SqlParameter("@uid", uid));
                    updateStageCommand.Parameters.Add(new SqlParameter("@relatedStage", relatedStage));
                    updateStageCommand.ExecuteNonQuery();
                }
            }
            relatedReader.Dispose();
            if (!hasHigherStage)
            {
                sqlCommand = new SqlCommand("DELETE FROM Po_sign WHERE rid = @rid");
                sqlCommand.Connection = sqlConnection;
                sqlCommand.Parameters.Add(new SqlParameter("@rid", rid));
                sqlCommand.ExecuteNonQuery();
            }
            sqlConnection.Close();
        }

第二種寫法是 再從前端多抓些資料過來操作,但還是一樣問題:

public void DeletePoSignById(string rid,string uid,int stage)
        {
            SqlConnection sqlConnection = new SqlConnection(ConnStr);
            sqlConnection.Open();

            SqlCommand sqlCommand = new SqlCommand("SELECT * FROM Po_sign WHERE uid = @uid");
            sqlCommand.Connection = sqlConnection;
            sqlCommand.Parameters.Add(new SqlParameter("@uid", uid));

            SqlDataReader relatedReader = sqlCommand.ExecuteReader();
            bool hasHigherStage = false;
            while (relatedReader.Read())
            {
                int relatedStage = Convert.ToInt32(relatedReader["stage"]);
                if (relatedStage > stage)
                {
                    hasHigherStage = true;
                    int updatedStage = relatedStage - 1;
                    SqlCommand updateStageCommand = new SqlCommand("UPDATE Po_sign SET stage = @updatedStage WHERE uid = @uid AND stage = @relatedStage");
                    updateStageCommand.Connection = sqlConnection;
                    updateStageCommand.Parameters.Add(new SqlParameter("@updatedStage", updatedStage));
                    updateStageCommand.Parameters.Add(new SqlParameter("@uid", uid));
                    updateStageCommand.Parameters.Add(new SqlParameter("@relatedStage", relatedStage));
                    updateStageCommand.ExecuteNonQuery();
                }
            }
            relatedReader.Dispose();
            if (!hasHigherStage)
            {
                sqlCommand = new SqlCommand("DELETE FROM Po_sign WHERE rid = @rid");
                sqlCommand.Connection = sqlConnection;
                sqlCommand.Parameters.Add(new SqlParameter("@rid", rid));
                sqlCommand.ExecuteNonQuery();
            }
            sqlConnection.Close();
        }

最後我是用下列程式解決問題;
但因為是直接換寫法,所以上面的問題我還是不懂哪裡有誤!

        public void DeletePoSignById(string rid, string uid, int stage)
        {
            SqlConnection sqlConnection = new SqlConnection(ConnStr);
            sqlConnection.Open();
            SqlCommand sqlCommand = new SqlCommand(
                " DECLARE @tmp TABLE (rid varchar(50)) " +
                " INSERT INTO @tmp " +
                " SELECT rid FROM Po_sign WHERE uid = @uid And stage > @stage " +
                " WHILE EXISTS(SELECT rid FROM @tmp) " +
                " BEGIN " +
                " UPDATE Po_sign SET stage = stage - 1 WHERE uid = @uid AND rid = (SELECT TOP 1 rid FROM @tmp) " +
                " DELETE FROM @tmp WHERE rid = (SELECT TOP 1 rid FROM @tmp) " +
                " END " +
                " DELETE FROM Po_sign WHERE rid = @rid AND uid = @uid AND stage = @stage ");
            sqlCommand.Connection = sqlConnection;
            sqlCommand.Parameters.Add(new SqlParameter("@rid", rid));
            sqlCommand.Parameters.Add(new SqlParameter("@uid", uid));
            sqlCommand.Parameters.Add(new SqlParameter("@stage", stage));
            sqlCommand.ExecuteNonQuery();
            sqlConnection.Close();
        }
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

1
wdchiang
iT邦新手 2 級 ‧ 2023-10-03 14:26:15
最佳解答

第二種寫法效能比較好,建議用第二種,SQL 可以這樣串

            SqlCommand sqlCommand = new SqlCommand(
                @" DECLARE @tmp TABLE (rid varchar(50)) 
                 INSERT INTO @tmp
                 SELECT rid FROM Po_sign WHERE uid = @uid And stage > @stage 
                 WHILE EXISTS(SELECT rid FROM @tmp) 
                 BEGIN 
                 UPDATE Po_sign SET stage = stage - 1 WHERE uid = @uid AND rid = (SELECT TOP 1 rid FROM @tmp) 
                 DELETE FROM @tmp WHERE rid = (SELECT TOP 1 rid FROM @tmp) 
                 END
                 DELETE FROM Po_sign WHERE rid = @rid AND uid = @uid AND stage = @stage ");

第一種改法:

            SqlConnection sqlConnection1 = new SqlConnection(ConnStr);
            sqlConnection1.Open();

            while (relatedReader.Read())
            {
                int relatedStage = Convert.ToInt32(relatedReader["stage"]);
                if (relatedStage > stage)
                {
                    hasHigherStage = true;
                    int updatedStage = relatedStage - 1;
                    SqlCommand updateStageCommand = new SqlCommand("UPDATE Po_sign SET stage = @updatedStage WHERE uid = @uid AND stage = @relatedStage");
                    updateStageCommand.Connection = sqlConnection1;
                    updateStageCommand.Parameters.Add(new SqlParameter("@updatedStage", updatedStage));
                    updateStageCommand.Parameters.Add(new SqlParameter("@uid", uid));
                    updateStageCommand.Parameters.Add(new SqlParameter("@relatedStage", relatedStage));
                    updateStageCommand.ExecuteNonQuery();
                }
            }

new 一個 connection 就可以。

1
科科
iT邦研究生 1 級 ‧ 2023-10-03 12:39:59

我記得同一條連線只能執行一次查詢,所以SqlConnection要先關閉在打開來使用。
就是在第一個 reader.Close(); 後面多加sqlConnection.Close();
後面要用在打開。

然後使用ADO.NET建議用using陳述式:

using (SqlConnection sqlConnection = new SqlConnection(ConnStr))
{
    sqlConnection.Open();
    // 執行 SQL 查詢
}
rain_yu iT邦新手 1 級 ‧ 2023-10-03 13:27:04 檢舉

用using的方式我也用過,但是還是錯誤,原因一樣。
原來是SqlConnection要先關閉在打開來使用ORZ
我原本也想會不會是這個問題,也有重新開開關關過,
沒有改成功。

我要發表回答

立即登入回答