SqlDataReader重复读取同一数据异常原因及解决方法咨询
问题分析与解决方案
咱们先拆解你遇到的问题:为什么调用GetSqlBytes和GetValue读取同一单元格会抛出异常,哪怕调换顺序也不行?
核心原因
这里的关键坑点在于SqlDataReader对大型二进制列(比如varbinary(max))的流式读取机制:
- 当你读取这类大型列时,Reader会把数据以流的形式提供,而非一次性加载到内存。
- 不管你用
GetSqlBytes还是GetValue,第一次读取后,这个流就会被消耗(流的指针走到末尾),后续再尝试读取同一列,就会因为流已耗尽而抛出异常。 - 你提到
SqlBytes继承自object,这没错,但SqlBytes是一个绑定到Reader流的包装对象——它并没有把数据复制到内存,而是直接引用Reader的数据流。当你后续调用GetValue时,Reader试图重新读取该列,但此时流已经被SqlBytes占用或已到末尾,自然会触发异常。
调试时第一次调用能拿到值,第二次抛异常,就是因为第一次读取已经把流的位置移动到了末尾,第二次读取没有数据可读了。
解决方案:只读取一次,内存内转换
既然同一列的流只能读取一次,那咱们只读取一次数据,然后在内存里转换成需要的两种类型就好,有两种方案可选:
方案1:先读SqlBytes,再转成byte[](作为object)
object o = null; SqlBytes bytes = null; try { // 仅读取一次列数据 bytes = ((SqlDataReader)dr).GetSqlBytes(iCol); // 将SqlBytes转换为byte[],作为object类型的值 o = bytes.ToArray(); // 若确定数据非空,也可直接用bytes.Value // 后续需要SqlBytes直接用已有的bytes对象即可,不用再调用Reader } catch (Exception ex) { MessageBox.Show($"异常信息:{ex.Message}"); }
方案2:先读GetValue得到byte[],再包装成SqlBytes
object o = null; SqlBytes bytes = null; try { // 先读取为object(实际类型是byte[]) o = dr.GetValue(iCol); // 从byte[]创建SqlBytes对象 if (o is byte[] byteArray) { bytes = new SqlBytes(byteArray); } } catch (Exception ex) { MessageBox.Show($"异常信息:{ex.Message}"); }
额外说明
如果你的列是小型二进制类型(比如varbinary(8000)及以下),其实是可以多次读取同一列的——因为这类数据会被Reader一次性加载到内存,而非流式处理。但对于max级别的大型列,就必须遵循“一次读取,内存转换”的原则。
内容的提问来源于stack exchange,提问作者Hello Everyone
相关产品推荐
相关产品推荐

