修复错误 - 已经有一个与此命令相关联的打开的DataReader,必须先关闭它。

4
在一个C#脚本中,我尝试使用SqlDataReader对象读取一个表并删除该表。 确实很简单。
这是我使用的代码 -
SqlConnection conn = getAWorkingDbConnection();//Always gives me a good connection
SqlCommand sqlCmd = new SqlCommand();
SqlDataReader dataReader;

sqlCmd.CommandTimeout = 0;
sqlCmd.Connection = conn;

sqlCmd.CommandText = "SELECT * FROM GlassTable";
dataReader = sqlCmd.ExecuteReader();

//Code to read rows with SqlDataReader and print them to a file.

sqlCmd.CommandText = "DROP TABLE GlassTable";
sqlCmd.ExecuteReader();// BAD !!!

我遇到了这个错误 - System.InvalidOperationException:与此命令关联的数据读取器已经打开,必须先关闭。

我查看了ExecuteReader方法的API,但它没有解决我的问题。为什么会出现这个错误?我该如何修复它?

谢谢。


我不想为此使用任何MARS。 - Steam
3
创建一个新命令,使用一个新连接。但是你能解释一下为什么不用MARS吗? - Steve
@Steve - 其实这段代码是在 SSIS C# 脚本中使用的。我使用了一个 SSIS(ETL 工具)数据库连接管理器,我怀疑它不允许你使用 MARS。如果你自己生成字符串,可以使用 mars。但是,如果你从 SSIS 连接管理器中提取它,可能不能使用。 - Steam
3个回答

7

你的问题在于没有处理你正在使用的对象。为此,最好总是使用using结构,因为它会保证所有内容都将被处理。尝试下面的代码:

sqlCmd.CommandText = "SELECT * FROM GlassTable";
using (dataReader = sqlCmd.ExecuteReader())
{
    //Code to read rows with SqlDataReader and print them to a file.
}

此外,如果查询不返回记录,您不必使用ExecuteReader
sqlCmd.CommandText = "DROP TABLE GlassTable";
int recordsAffected = sqlCmd.ExecuteNonQuery();

哦,是的。它是一个非查询。 - Steam
代码不起作用。我再次得到了相同的错误。 - Steam
@blasto 你确定把所有的 IDataReader 都放在一个单独的 using 块中,并且将 ExecuteNonQuery() 放在这些块之外了吗?当 using 块结束时,读取器会关闭。 - C.Evenhuis

2

使用using语句尽快处理所有未托管的资源:

using(SqlConnection conn = getAWorkingDbConnection())
using (SqlCommand sqlCmd = new SqlCommand("SELECT * FROM GlassTable", conn))
{
    sqlCmd.CommandTimeout = 0;
    conn.Open();
    using (SqlDataReader dataReader = sqlCmd.ExecuteReader())
    {
        while (dataReader.Read())
        { 
            // do something useful ...
        }
    }
    sqlCmd.CommandText = "DROP TABLE GlassTable";
    sqlCmd.ExecuteNonQuery();
}

1
@blasto:不,这个 using 的作用域包括下一个 using,类似于 foreach(var obj in seq)foreach(var otherObj in obj.OtherSeq){}。我可以用 {} 包围它。我更喜欢这样做来避免缩进。 - Tim Schmelter
@blasto:是的,如果你愿意甚至可以在using之前声明它。using语句只是确保即使出现错误也会处理IDisposable(同时关闭连接)。 - Tim Schmelter
只是为了确保我说的正确 - SqlCommand sqlCmd = new SqlCommand(); 使用 (sqlCmd = getCommandFromSomewhere()) {//在这里使用它}。 这样正确吗? - Steam
@blasto:没问题。但是new SqlCommand()是多余的,不是吗?而且如果它返回一个命令,那么getConnFromSomewhere的名称是错误的。 - Tim Schmelter
Tim,我用你的代码也遇到了同样的错误。我会在这里发布整个代码。 - Steam
显示剩余3条评论

1
你需要关闭数据读取器。
最快的方法是使用using语句来为您处理数据读取器的Dispose操作:
using (var dataReader = sqlCmd.ExecuteReader())
{
      //Stuff
}

或者如果您想保留相同的实例,可以在try...finally块中调用Close().


1
今晚我的英语真差,@Selman22,这样读起来更好吗? - DaveShaw

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