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 必须关闭的主要内容,如果未能解决你的问题,请参考以下文章
在 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