必须关闭asp.net datareader

时间:2011-12-14 09:16:34

标签: c# asp.net datareader

我的代码:

SqlConnection con = new SqlConnection(WebConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString);
SqlCommand cmd = new SqlCommand();
//..........
cmd.CommandText = "SELECT * FROM TempQn WHERE creatorId=  '" +
Session["administratorID"].ToString() + "'";  
dr = cmd.ExecuteReader();  
while (dr.Read())
{
    int ids = Int32.Parse(dr["QuestionID"].ToString());
    cmd.CommandText = " INSERT INTO Answers (QuestionId,Answer) Select c.QnId, c.Answer From TempAns c Where c.Id = " + ids + " ";
    cmd.ExecuteNonQuery(); //this line
}
dr.Close();

错误是:

  

已经有一个与此命令关联的打开DataReader,必须先关闭它。

什么样的命令应该取代cmd.ExecuteNonQuery();

5 个答案:

答案 0 :(得分:3)

只要DataReader处于“活动状态”,就无法执行任何其他SQL语句。

为了解决这个问题,存储SQL语句列表然后在阅读后执行它们:

cmd.CommandText = "SELECT * FROM Question WHERE SurveyID= '" + sID + "'";    
dr = cmd.ExecuteReader();  
List<string> arrSQL = new List<string>();
while (dr.Read())
{
    int ids = Int32.Parse(dr["QuestionID"].ToString());
    arrSQL.Add("INSERT INTO Answers (QuestionId,Answer) Select c.QnId, c.Answer From TempAns c Where c.Id = " + ids + " ");
}
dr.Close();

arrSQL.ForEach(strSQL =>
{
    cmd.CommandText = strSQL;
    cmd.ExecuteNonQuery();
});

您当前的代码很容易受到SQL注入攻击,并且不是很好的做法 - 您最好使用Parameter而不是为原始SQL注入值 - 这是如何实现的:

cmd.CommandText = "SELECT * FROM Question WHERE SurveyID=@id";
cmd.Parameters.AddWithValue("@id", sID);
dr = cmd.ExecuteReader();  
List<int> arrQuestions = new List<int>();
while (dr.Read())
{
    int ids = Int32.Parse(dr["QuestionID"].ToString());
    arrQuestions.Add(ids);
}
dr.Close();

cmd.CommandText =  "INSERT INTO Answers (QuestionId, Answer) Select c.QnId, c.Answer From TempAns c Where c.Id = @id";
arrQuestions.ForEach(id =>
{
    cmd.Parameters["@id"].Value = id;
    cmd.ExecuteNonQuery();
});

答案 1 :(得分:1)

您已经有一个与“cmd”关联的命令。

  dr = cmd.ExecuteReader();  
          while (dr.Read())
          {
              int ids = Int32.Parse(dr["QuestionID"].ToString());
              SqlCommand sqlCmd = new SqlCommand("INSERT INTO Answers (QuestionId,Answer) Select c.QnId, c.Answer From TempAns c Where c.Id = " + ids + " ");
              sqlCmd.ExecuteNonQuery(); //this line
          }
          dr.Close();

所以就像上面给出的那样,为插入创建一个新命令。

答案 2 :(得分:1)

这个单一查询应该完成这项工作(不确定您是否准确的数据模型,如果需要,可以调整):

INSERT INTO Answers (QuestionId,Answer) 
Select c.QnId, c.Answer 
From TempAns c 
inner join Question q on c.QnId = q.Id
where q.SurveyID = @SurveyID

为了避免SQl注入,请使用此C#代码:

cmd.CommandTest = @"INSERT INTO Answers (QuestionId,Answer) 
    Select c.QnId, c.Answer 
    From TempAns c 
    inner join Question q on c.QnId = q.Id
    where q.SurveyID = @SurveyID";

SqlParameter param = cmd.Parameters.Add("@SurveyID", SqlDbType.Int);
param.Value = yourSurveyId;

cmd.Open(); // it would be better to check the status before 
cmd.ExecuteNonQuery();
cmd.Close();

答案 3 :(得分:1)

您可以更改连接字符串,而不是使用第二个连接对象,并使用MARS(Multiple active result set)来实现此目的。将以下语句添加到连接字符串:

MultipleActiveResultSets=True

编辑: 和其他人说的那样,使用SqlParameters作为参数而不是字符串连接。这不仅是一个安全问题,而且还有巨大的性能损失!

答案 4 :(得分:0)

您需要声明一个新的命令对象,因为当您尝试将其用于insert语句时,cmd已用于读取数据。另外,不要使用sql命令中的字符串连接,这是一种不好的做法,容易受到SQL注入攻击。使用参数。