asp.net datareader 必须关闭

Posted

技术标签:

【中文标题】asp.net datareader 必须关闭【英文标题】:asp.net datareader must be closed 【发布时间】:2011-12-14 09:16:34 【问题描述】:

我的代码:

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

【问题讨论】:

首先,使用字符串连接构建 sql 查询是一种不好的做法,因为它可能导致安全漏洞。请改用 SqlParameters。 第二,你不能在一个 sql 查询中做你正在做的事情吗? 我猜你打开另一个数据阅读器代替......? 第三:您必须打开一个新的 SQL 连接,或者从 DataTable 中的第一个查询中加载整个数据(例如),然后处理每一行。 请不要将解决方案作为问题,这会引起混淆 - 阅读问题的人需要查看原始问题,然后他们才能阅读答案,并且接受的答案(如果存在)是正确的解决方案。您可以添加更多详细信息,但不要覆盖原始问题。 【参考方案1】:

只要 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();
);

【讨论】:

我不会使用 datareader 循环来填充 List,而是使用 SqlTableAdapter 来填充 DataTable。它将更多地包含 ADO.Net 类 @SteveB 在这里看不到任何性能提升,这是个人喜好 - DataReader 是访问数据的最直接方式。 这个想法不是为了提高性能,而是坚持使用常见的 ado.net 类【参考方案2】:

您已经有一个与“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();

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

【讨论】:

它说:ExecuteNonQuery:连接属性尚未初始化。 SqlCommand sqlCmd = new SqlCommand("INSERT INTO Answers (QuestionId,Answer) Select c.QnId, c.Answer From TempAns c where c.Id = " + ids + " ",con);您所要做的就是将 SqlConnection 作为第二个参数传递【参考方案3】:

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

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

【讨论】:

对不起,我忘了更改第一个选择子句。你能看一下吗?因为 TempAns 和 Question 表之间不相关(没有 FK) 不需要 FK 来连接表,即使它通常更可取【参考方案4】:

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

MultipleActiveResultSets=True

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

【讨论】:

【参考方案5】:

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

【讨论】:

以上是关于asp.net datareader 必须关闭的主要内容,如果未能解决你的问题,请参考以下文章

如何找到未关闭的DataReader?

在 ASP.NET 中拉多个记录集时的 DataReader 或 DataSet

在标签控件 (ASP.NET) 中显示来自 DataReader 的数据

Asp.net常用开发方法之DataTable/DataReader转Json格式代码

ASP.NET MVC 项目中 ADO.NET 实体模型的已打开 DataReader

在 ASP.Net 中使用 SQL Server 2012 地理数据类型 - DataReader.GetFieldType(x) 返回 null