1

这个问题与我之前的一个答案有些相关,它帮助我找到了通过 SqlDataReader 进行这个简单迭代的原因:

m_aFullIDList = New Generic.List(Of Integer)

While i_oDataReader.Read
    m_aFullIDList.Add(i_oDataReader.GetInt32(0))
End While

m_iTotalNumberOfRecords = m_aFullIDList.Count

不返回所有记录。结果,当 Generic.List 更改其容量以容纳更多元素(例如,从 2^19 到下一个 2^20)时,此时 SqlDataReader 简单退出,其 Read 方法返回 False,就好像没有更多记录一样。

大多数时候它会安静地退出,不会抛出任何异常。但时不时我会得到:

NullReferenceException {“对象引用未设置为对象的实例。”}

在 System.Data.SqlClient.SqlDataReader.ReadColumnHeader(Int32 i)
在 System.Data.SqlClient.SqlDataReader.ReadColumn(Int32 i, Boolean setTimeout) 在 System.Data.SqlClient.SqlDataReader.GetInt32(Int32 i)

我知道 Reader 使用的存储过程返回的所有记录(它是单列)都是整数值。如果我删除行m_aFullIDList.Add并将简单的读取值转换为整数变量,或者如果我将通用列表容量预分配给一个已知的大数 - 这个问题就不会发生。显然它只发生在 List 重新分配容量时 - 这会影响读者。

一旦这个结构的容量重新分配超过某个点,我还尝试使用其他结构(ArrayList,甚至 Array,使用 Array.Resize) - 这会破坏 SqlDataReader。

这个 ASP.NET 项目有点复杂,所以当我尝试在一个独立的简单项目中重新创建问题时,该项目仅包含执行阅读器和读入列表 - 问题没有发生。知道发生了什么以及如何解决这个问题吗?

4

1 回答 1

0

我想我终于想通了。

我的代码中的逻辑有 2 个步骤 - 调用返回 SqlDataReader 的函数,然后在另一个函数中使用该读取器来填充列表:

Dim oReader as SqlDataReader = GetTheReader()

FillTheList(oReader)

函数 GetTheReader() 看起来像这样:

Function GetTheReader() as SqlDataReader
   Dim oConn As New SqlConnection("Connection String") : oConn.Open()
   Dim oComm As New SqlCommand("Stored Procedure", oConn)

   Dim oReader As SqlDataReader = oComm.ExecuteReader(CommandBehavior.CloseConnection)

   Return oReader
End Function

它以局部变量的形式打开连接,当函数返回给调用者时,该变量超出了范围。并且当填充通用列表时,在另一个容量分配垃圾收集通过销毁过时的变量(并关闭该连接)来声明该内存。我留下了一个没有有效连接的 SqlDataReader。

我目前的解决方案是在函数外部创建连接GetTheReader并将其作为参数之一传递。

于 2013-08-29T20:42:37.887 回答