【问题标题】:Close SqlDataReader if there are no more rows left to read - C#如果没有更多要读取的行,则关闭 SqlDataReader - C#
【发布时间】:2017-09-05 09:04:21
【问题描述】:

这让我很头疼。我知道这个问题(或至少它的变体)已被问过很多次,但在将其标记为重复之前,请考虑以下代码:

    string myConnectionString =  myConnectionString = ConfigurationManager.ConnectionStrings["DBCS"].ToString();
     SqlConnection mySQLConnection;
     SqlCommand mySQLCommand;
     SqlDataReader mySQLDataReader;

    using (mySQLConnection = new SqlConnection(myConnectionString))
    {
       mySQLCommand = new SqlCommand("SELECT TOP 1 * FROM Table ORDER BY Id DESC", mySQLConnection);
       mySQLCommand.Connection = mySQLConnection;
       mySQLCommand.Connection.Open();
       using(mySQLDataReader = mySQLCommand.ExecuteReader())
       {
         if (mySQLDataReader.HasRows)
         {
             if (mySQLConnection.State == ConnectionState.Open)
             {
                while (mySQLDataReader.Read())
                {
                  //Perform Logic : If the last record being returned meets some condition then call the below method
                   MethodCalled();
                }            
            }
         }
      }
         MessageBox.Show("Connection state: " + mySQLConnection.State);
   }

我想找到一种方法:

  1. 读完后关闭读卡器
  2. 当 while-loop 读完并且没有剩余行时,退出它

但我只是不断收到SqlException 说明以下内容:

阅读器关闭时调用 read 的尝试无效

仅从广泛的观察来看,我可以追踪到该错误是由于我返回的数据仅包含一行。问题在于,在读取该行之后,编译器会返回到While(mySQLDataReader.Read()){} 并尝试读取一个不包含任何行的表。

我尝试了以下方法:

  1. 将命令对象中的ExecuteReader() 包装在using 块中,以便在完成读取后分别自动关闭读取器和连接:

    using(mySQLDataReader = mySQLCommand.ExecuteReader())
    {
        //Logic performed
    }
    
  2. 就在while-loop 的右大括号之前,我尝试检查是否有更多行从 sql 命令留下/返回,并在满足该条件后中断循环:

    if(mySQLDataReader.HasRows == false) //No more rows left to read
    {
         break; //break out of loop
    }
    

两次尝试均未成功。我该如何解决这个问题?

【问题讨论】:

  • 这很奇怪。我已经使用过无数次了。 Read() 函数在数据末尾返回 false 并且不抛出异常。你能发布更多代码让我们看看你在做什么吗?
  • 您能否发布一个突出您的问题的最小示例?
  • 能否提供更多代码?看来您正在尝试将Read 超出using(...) {} 范围:using(mySQLDataReader = mySQLCommand.ExecuteReader()) {...} ... mySQLDataReader.Read();
  • 您能否发布您正在使用的复制此错误的整个代码块?
  • 你用//Logic performed替换的代码是什么?它应该类似于while(mySQLDataReader.Read()) { /* do stuff with rows */ }。 Read() 方法在还有要读取的行时返回 true,在读取最后一行时返回 false。

标签: c# winforms ado.net


【解决方案1】:

它必须是以下三件事之一:

  1. 您在using 块之外使用Read()。请记住,using 块将隐式调用您的阅读器上的 Close 和 Dispose。因此,任何Read() 调用都必须放在using 块内。
  2. using 块的主体明确关闭了阅读器。这似乎不太可能。
  3. 显然您已在更高级别声明了您的mySQLDataReader。可能是其他一些(异步)代码正在关闭阅读器。这也是不太可能的。在大多数情况下,您不应该在全局级别定义 DataReader。

编辑

阅读您现在发布的完整代码块,我建议进行一些更改。您能否运行以下命令并告诉我们它是否运行:

using (var mySQLConnection = new SqlConnection(myConnectionString))
{
   mySQLCommand = new SqlCommand("SELECT TOP 1 * FROM Table ORDER BY Id DESC", mySQLConnection, mySQLConnection);
   mySQLCommand.Connection.Open();
   using(mySQLDataReader = mySQLCommand.ExecuteReader())
   {
      while (mySQLDataReader.Read())
      {
        //Perform Logic : If the last record being returned meets some condition then call the below method
         MethodCalled();
      }            
   }
}

如果这个版本运行良好,我们可以更好地挖掘问题。

【讨论】:

  • 2.我不会在 while 循环中的任何地方调用 Dispose 或 Close
  • 3.是的,我已经在全局范围内声明了 DataReader 对象,并且我在我的其他函数中使用它没有任何问题,那为什么现在会出现问题?
  • 我在while-loop 的右大括号之前添加了它,它按预期工作。您可以在测试后更新您的答案。 if(mySQLDataReader.HasRows == false || mySQLDataReader.Read() == false) { mySQLDataReader.Close(); break; }
【解决方案2】:

如果没有要迭代的数据,while 循环将根本不会执行。您是否需要检查 HasRows 本身?此外,您应该在创建数据读取器时使用 CommandBehavior.CloseConnection。这将确保在您通读后关闭底层连接。

Should if call SqlDataReader.HasRows if I am calling SqlReader.Read

SQLDataReader Source Code

using (SqlConnection mySQLConnection = new SqlConnection(myConnectionString))
{
using (SqlCommand mySQLCommand = new SqlCommand("SELECT TOP 1 * FROM Table ORDER BY Id DESC", mySQLConnection))
  {
    mySQLConnection.Open();

    SqlDataReader mySQLDataReader = mySQLCommand.ExecuteReader(CommandBehavior.CloseConnection);
     while (mySQLDataReader.Read())
       {
          //Code logic here
        }
        // this call to mySQLDataReader.Close(); will close the underlying connection
         mySQLDataReader.Close();
    }
     MessageBox.Show("Connection state: " + mySQLConnection.State);
}

【讨论】:

  • 所以事实证明使用mySQLDataReader.HasRows) 是一种很好的做法,因此我没有从问题中省略它。
  • 如果你想用它做点什么,在 if 条件内。
  • 我在while-loop 的右大括号之前添加了它,它按预期工作。您可以在测试后更新您的答案。 if(mySQLDataReader.HasRows == false || mySQLDataReader.Read() == false) { mySQLDataReader.Close(); break; }
  • Read() 将 SqlDataReader 光标增强到下一条记录,因此,当您必须对记录进行实际操作时,请不要在 if 条件下使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 2011-09-04
  • 2013-10-27
相关资源
最近更新 更多