错误'已经存在与此命令相关联的打开数据读取器,必须先关闭'。

25

运行时错误 '已经有一个与此命令关联的打开数据读取器,必须先关闭'

objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn);

objDataReader = objCommand.ExecuteReader();

while (objDataReader.Read())
{
objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + objDataReader[0] + "')", objConn);
objInsertCommand.ExecuteNonQuery();//Here is the error
}
objDataReader.Close();

我无法在这里定义任何存储过程。 希望得到帮助。


请参考以下链接:https://dev59.com/-mMl5IYBdhLWcg3wV10F#20535263 - LCJ
12个回答

81

无需做这些,只需启用MARS即可解决问题。在您的连接字符串中添加MultipleActiveResultSets=True;


1
已经在 SQL 2008 中验证过了。http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataproviders/thread/78d3989a-8975-4930-998d-1eb907966f57 - user423430
1
适用于我。SQL Server 2008。 - finitenessofinfinity
对我来说可以。SQL Server 2008。 - Swag
当在CLR过程中使用"context connection=true"时,无法使用MARS:http://social.msdn.microsoft.com/Forums/en-US/2575accc-72ac-4fab-99c1-1683afa39d72/context-connection-and-multipleactiveresultsets-can-have-both-at-the-same-time?forum=sqlnetfx - Rafael Emshoff
请参考以下链接:https://dev59.com/-mMl5IYBdhLWcg3wV10F#20535263 - LCJ
我在 SQL Server Express 2008 上验证了这个问题,这是在迁移 SQL Server Compact Edition 3.5 安装后出现的。 - dlopezgonzalez

7

当连接正在读取数据读取器的内容时,您无法对该连接执行操作 - 错误非常描述性。

您的替代方案是:

1)首先检索所有数据,可以使用DataSet或使用读取器填充其他集合,然后在初始选择完成后一次运行它们。

2)为插入语句使用不同的连接。


6
如何通过Fill将数据拉入DataSet,然后遍历DataSet以通过NonQuery执行插入操作?
IDbDataAdapter da;
IDbCommand selectCommand = connection.CreateCommand();
selectCommand.CommandType = CommandType.Text;
selectCommand.CommandText = "SELECT field1, field2 FROM sourcetable";
connection.Open();
DataSet selectResults= new DataSet();
da.Fill(selectResults); // get dataset
selectCommand.Dispose();
IDbCommand insertCommand;

foreach(DataRow row in selectResults.Tables[0].Rows)
{
    insertCommand = connection.CreateCommand();
    insertCommand.CommandType = CommandType.Text;
    insertCommand.CommandText = "INSERT INTO tablename (field1, field2) VALUES (3, '" + row["columnName"].ToString() + "'";   
}
insertCommand.Dispose();
connection.Close();

请问您能否提供语法或示例? - Pradeep

4

你可以先将需要的信息读入到一个列表中,然后对该列表进行迭代以执行插入操作,具体如下:

        List<String> values = new List<String>();
        using(SqlCommand objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn)) {
            using(SqlDataReader objDataReader = objCommand.ExecuteReader()) {
                while(objDataReader.Read()) {
                    values.Add(objDataReader[0].ToString());
                }
            }
        }
        foreach(String value in values) {
            using(SqlCommand objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + value + "')", objConn)) {
                objInsertCommand.ExecuteNonQuery();
            }
        }

到目前为止,唯一一个安全处理命令和读取器的答案得到了+1的评价。 - PHeiberg

3
INSERT INTO tablename (field1, field2)
    SELECT 3, field1 FROM sourcetable

使用单个SQL语句代替每个插入操作。不确定是否适用于您的实际问题,但对于您提供的示例,这比逐个执行要好得多。

另外,请确保您的代码使用参数化查询,而不是在SQL语句中直接接受字符串 - 您的示例容易受到SQL注入攻击。


2

已经提出了几个非常有效的建议,以及改进实现的建议。由于现有代码没有清理阅读器,我遇到了MARS限制,因此我想组合一个更加可靠的示例:

const string connectionString = @"server=.\sqlexpress;database=adventureworkslt;integrated security=true";
const bool useMARS = false;
using (var objConn = new System.Data.SqlClient.SqlConnection(connectionString + (useMARS ? ";MultipleActiveResultSets=True" : String.Empty)))
using (var objInsertConn = useMARS ? null : new System.Data.SqlClient.SqlConnection(connectionString))
{
 objConn.Open();
 if (objInsertConn != null)
 {
  objInsertConn.Open();
 }

 using (var testCmd = new System.Data.SqlClient.SqlCommand())
 {
  testCmd.Connection = objConn;
  testCmd.CommandText = @"if not exists(select 1 from information_schema.tables where table_name = 'sourcetable')
                          begin
                           create table sourcetable (field1 int, field2 varchar(5))
                           insert into sourcetable values (1, 'one')
                           create table tablename (field1 int, field2 varchar(5))
                          end";
  testCmd.ExecuteNonQuery();
 }

 using (var objCommand = new System.Data.SqlClient.SqlCommand("SELECT field1, field2 FROM sourcetable", objConn))
 using (var objDataReader = objCommand.ExecuteReader())
 using (var objInsertCommand = new System.Data.SqlClient.SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, @field2)", objInsertConn ?? objConn))
 {
  objInsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("field2", String.Empty));
  while (objDataReader.Read())
  {
   objInsertCommand.Parameters[0].Value = objDataReader[0];
   objInsertCommand.ExecuteNonQuery();
  }
 }
}

2

选项1: 在运行另一个查询之前,必须执行查询并加载数据。

选项2: 在连接字符串的提供程序部分添加MultipleActiveResultSets=true。请参见以下示例:

<add name="DbContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />

1
你使用的SQL Server版本是什么?问题可能出在这里:
(来自http://msdn.microsoft.com/en-us/library/9kcbe65k.aspx
当您使用SQL Server 2005之前的版本时,当SqlDataReader正在使用时,相关的SqlConnection正在忙于为SqlDataReader提供服务。在此状态下,除了关闭它之外,不能对SqlConnection执行任何其他操作。直到调用SqlDataReader的Close方法为止。
因此,如果这是导致问题的原因,您应该先读取所有数据,然后关闭SqlDataReader,然后再执行插入操作。
类似这样:
objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn);

objDataReader = objCommand.ExecuteReader();

List<object> values = new List<object>();
while (objDataReader.Read())
{
    values.Add(objDataReader[0]);
}

objDataReader.Close();

foreach (object value in values)
{
    objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + value + "')", objConn);
    objInsertCommand.ExecuteNonQuery();
}

1
将此添加到连接字符串中应该可以解决问题。
MultipleActiveResultSets=true

0
最佳解决方案: 你的“CommandText”值只有一个问题。无论是SP还是普通的Sql查询,都可以使用它。
  • 检查1:在执行ExecuteReader时,您传递给Sql查询的参数值没有改变,而是一遍又一遍地传递相同的值。

  • 检查2:Sql查询字符串格式错误。

  • 检查3:请按以下方式创建最简单的代码。

    string ID = "C8CA7EE2";
    string myQuery = "select * from ContactBase where contactid=" + "'" + ID + "'";
    string connectionString = ConfigurationManager.ConnectionStrings["CRM_SQL_CONN_UAT"].ToString(); 
    SqlConnection con = new SqlConnection(connectionString);
    con.Open();
    SqlCommand cmd = new SqlCommand(myQuery, con);
    DataTable dt = new DataTable();
    dt.Load(cmd.ExecuteReader());
    con.Close();
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接