【问题标题】:"There is already an open DataReader associated with this Command which must be closed first."“已经有一个打开的 DataReader 与此命令关联,必须先关闭它。”
【发布时间】:2017-07-15 07:32:20
【问题描述】:

我正在开发需要连接到另一个数据库以获取一些数据的应用程序, 为此,我决定使用 SqlConnection、阅读器等。

我需要执行一些查询,例如首先我需要获取某个用户的 CARD ID,然后我需要通过该 CARD ID 获取一些数据..

这是我的代码:

#region Connection to another Database

SqlConnection sqlConnection1 = new SqlConnection("Data Source=ComputerOne; Initial Catalog=TestDatabase;Integrated Security=False; User ID=test; Password=test123;");
SqlCommand cmd = new SqlCommand();
SqlDataReader reader;

cmd.CommandText = "Select * From Users Where CardID=" + "'" + user.CardID + "'";
cmd.CommandType = CommandType.Text;
cmd.Connection = sqlConnection1;

sqlConnection1.Open();

reader = cmd.ExecuteReader();

string cardID = "";
string quantity="";

while (reader.Read())
{
    cardID = reader["CardID"].ToString();
}
//HOW COULD I WRITE ANOTHER QUERY NOW, FOR EXAMPLE, OK I GOT CARDID NOW GIVE ME SOME OTHER THINGS FROM THAT DATABASE BY THAT cardID
//here I tried to change CommandText and to keep working with reader.. but its not working like this because its throwing me exception mention in question title.

cmd.CommandText = "Select T1.CardID, T2.Title, Sum(T1.Quantity) as Quantity From CardTransactions as T1 JOIN Adds as T2 ON T1.AddsID = T2.AddsID Where T1.CardID =" + cardID + "AND T1.Type = 1 Group By T1.CardID, T2.Title";

reader = cmd.ExecuteReader();

while (reader.Read())
{
    quantity = reader["Quantity"].ToString();
}

// Data is accessible through the DataReader object here.

sqlConnection1.Close();

#endregion 

那么伙计们,我怎样才能使用这个示例执行一些查询语句。

非常感谢! 干杯

【问题讨论】:

标签: c# sql database connection reader


【解决方案1】:

您打开的阅读器仍然处于活动状态并处于打开状态。而且您一次只能有一个活跃的读者。您应该将所有 Sql... 实例包装在 using 中,以确保它们正确关闭。

using (SqlConnection connection = new SqlConnection(...))
{
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        // the code using reader
    }
}

【讨论】:

  • 那么我怎样才能执行 2 个或更多查询并用同一个阅读器阅读它们......?
  • 使用上面的代码可以,但不能同时使用2个。您关闭第一个,然后继续阅读第二个。
【解决方案2】:

嗯...您收到错误是因为第一次调用使用的阅读器未关闭。使用完 DataReader 对象后,您应该始终调用 Close 方法,以确保读取器使用的连接返回到连接池(该连接仅由该 DataReader 使用)。部分代码:

reader = cmd.ExecuteReader();
try
{
  while(myReader.Read()) 
  {
    while (reader.Read())
    {
        cardID = reader["CardID"].ToString();
    }
}
finally
{
  myReader.Close();
}
...
reader = cmd.ExecuteReader();
try
{
  while(myReader.Read()) 
  {
        reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            quantity = reader["Quantity"].ToString();
        }
  }
}
finally
{
  myReader.Close();
  myConnection.Close();
}

另外...作为一个干净的代码规则,用不同的方法分隔你的调用(SOLID 原则)

【讨论】:

  • 不,建议这样做很危险。如果出现异常,如果该行没有被执行,数据读取器将保持打开状态。
  • "在使用完 DataReader 对象后,您应该始终调用 Close 方法。如果您的 Command 包含输出参数或返回值,则在关闭 DataReader 之前它们将不可用。请注意,虽然DataReader 已打开,Connection 由该 DataReader 独占使用。在关闭原始 DataReader 之前,您无法为 Connection 执行任何命令,包括创建另一个 DataReader。 (取自msdn.microsoft.com/en-us/library/haa3afyz(v=vs.110).aspx
  • 另外……万一出现异常,可以好好利用try-catch机制
  • 然后在你的答案中解释。
  • 我不同意,给出一个有潜在危险的答案不是解决方案。 @levent
【解决方案3】:

你的问题是你没有处理你正在使用的对象。为此目的,最好始终使用using 结构,因为它可以保证你所有的东西都会被处理掉。试试下面的代码:

SqlConnection sqlConnection1 = new SqlConnection("Data Source=ComputerOne; Initial Catalog=TestDatabase;Integrated Security=False; User ID=test; Password=test123;");
SqlCommand cmd = new SqlCommand();
SqlDataReader reader;
string cardID = "";
string quantity="";

using(sqlConnection1 = new SqlConnection("Data Source=ComputerOne; Initial Catalog=TestDatabase;Integrated Security=False; User ID=test; Password=test123;"))
{
    sqlConnection1.Open();

    using(cmd = new SqlCommand())
    {
        cmd.CommandText = "Select * From Users Where CardID=" + "'" + user.CardID + "'";
        cmd.CommandType = CommandType.Text;
        cmd.Connection = sqlConnection1;

        using(reader = cmd.ExecuteReader())
        {


            while (reader.Read())
            {
                cardID = reader["CardID"].ToString();
            }
        } //reader gets disposed right here
    } //cmd gets disposed right here

    using(cmd = new SqlCommand())
    {
        cmd.CommandText = "Select T1.CardID, T2.Title, Sum(T1.Quantity) as Quantity From CardTransactions as T1 JOIN Adds as T2 ON T1.AddsID = T2.AddsID Where T1.CardID =" + cardID + "AND T1.Type = 1 Group By T1.CardID, T2.Title";
        cmd.CommandType = CommandType.Text;
        cmd.Connection = sqlConnection1;

        using(reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                quantity = reader["Quantity"].ToString();
            }
        } //reader gets disposed right here
    } //cmd gets disposed right here
    sqlConnection1.Close();
} //sqlConnection1 gets disposed right here

【讨论】:

  • 你能稍微解释一下为什么你把所有东西都放在使用中吗?我知道你为 new SqlConnection 做了确保一次打开一个连接,但为什么要使用其他所有东西?对于读者等等,我不能只用一个读者,那个 reader.Close();之后写 new cmd.CommandText="Select * from Users";阅读器=cmd.ExecuteReader();等等..关闭阅读器->编写新的cmd文本->将结果添加到阅读器..但还是感谢您的回答!
  • @Roxy'Pro 基本上,所做的是在 using 语句中使用 SqlConnection、SqlCommand 和 SqlDataReader。使用时,对象会在块完成后立即处理。所以您以后可以重复使用它们,我将编辑答案,以便您更好地看到它。就像您每次使用对象时调用 Dispose() 一样(这也可以解决这个问题,但“使用”是正确的方法)
  • @NicoRiff 一个问题是我一般应该在 using 语句中放入哪些对象,我的意思是在块完成后应该处理哪种对象,比如我怎么知道为什么应该在 using 语句中?
  • @NicoRiff 一个问题是我一般应该在 using 语句中放入哪些对象,我的意思是在块完成后应该处理哪种对象,比如我怎么知道为什么应该在 using 语句中?
猜你喜欢
  • 1970-01-01
  • 2011-08-29
  • 1970-01-01
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
相关资源
最近更新 更多