我应该将啥“长度”参数传递给 SqlDataReader.GetBytes()
Posted
技术标签:
【中文标题】我应该将啥“长度”参数传递给 SqlDataReader.GetBytes()【英文标题】:What 'length' parameter should I pass to SqlDataReader.GetBytes()我应该将什么“长度”参数传递给 SqlDataReader.GetBytes() 【发布时间】:2012-06-23 12:22:37 【问题描述】:我有一个 SqlDataReader,需要使用 SqlDataReader.GetBytes() 方法从中读取一个 varbinary(max) 列。此方法填充一个字节数组,因此需要知道要读取的数据长度。
这就是我感到困惑的地方.. 显然我想读取从数据库返回的所有数据在这一行/列中,那么我应该传递什么“长度”参数?
据我所知,SqlDataReader 没有提供任何方法来发现可用数据的长度,因此这种方法对我来说似乎相当尴尬。
我很想在这里传递 int.MaxValue 并忘记这个问题,但我对此并不满意。
我知道我可以改为调用
byte[] value = (byte[])dataReader["columnName"];
.. 这似乎完全解决了内部的长度问题。但是,我正在使用围绕 SqlDataReader.GetXXXX() 方法构建的一组复杂的代码生成模板。所以我对使用 GetBytes 很感兴趣,需要了解它的正确用法。
【问题讨论】:
SQL 有方法来询问 varbinary 中的长度 - 您必须将其放入 SQL:msdn.microsoft.com/en-us/library/ms173486.aspx - DATALENGTH。选择Field, DataLength(FIeld) from Table 就可以看到长度了。 谢谢,我确实考虑在 SQL 中使用 DATALENGTH,但这并不是一个真正可以接受的解决方案。正如您可能猜到的那样,这一切都构成了数据访问层的一部分,因此在手写存储过程中强制使用诸如此类的约定来支持基本功能并不是很好。 @Martyn 当我在这里时...有一些库专用来确保您不需要编写乏味的 ADO.NET 代码...“dapper” (在我的严重偏见中)非常适合隐藏这些担忧,同时保持非常轻量级且不打扰。 强制约定?没有最佳使用 SQL 层的 DAL 是一回事 - 损坏。这不是“惯例”,这是数据存储层的设计方式。 @Marc Gravell 我在这里维护一个遗留产品,因此提到了我不能真正偏离太多的代码模板。我多么想使用现成的 DAL 或 ORM!但这是你需要和我老板讨论的问题:) 【参考方案1】:在处理varbinary(max)
时,有两种情况:
GetBytes()
适用于 second 场景,当您使用 CommandBehaviour.SequentialAccess
来确保您流式传输数据,而不是缓冲时间>它。特别是,在这种用法中,您通常会(例如)在流中、在循环中写入。例如:
// moderately sized buffer; 8040 is a SQL Server page, note
byte[] buffer = new byte[8040];
long offset = 0;
int read;
while((read = reader.GetBytes(col, offset, buffer, 0, buffer.Length)) > 0)
offset += read;
destination.Write(buffer, 0, read); // push downstream
但是!如果您使用的是中等大小的数据,那么您的原始代码:
byte[] data = (byte[])reader[col];
很好!!。这种方法没有任何问题,事实上Get*
API 在某些情况下被破坏 - GetChar()
是一个值得注意的例子(提示:它不起作用)。
没关系您有使用 Get*
的现有代码 - 在这种情况下,强制转换方法非常合适。
【讨论】:
谢谢,这是一个很好的答案!在我的特殊情况下,整个数据将始终被读入一个字节数组,而不是缓冲或推送到另一个流中。在这种情况下,与仅调用一次 GetBytes 并传递 int.MaxValue 作为长度相比,手动编写读取循环是否有任何性能优势? @MartynReadBytes
是一个流式 API;您可以永远将一个大值传递给此类并假设它读取所有内容 - 您总是需要循环直到您返回一个非正值。我在这里的主要猜测(未选中)是 internally 它已经知道长度(至少在缓冲使用中),并且将分配一个大小合适的数组并只是块复制数据。如果您通过GetBytes()
执行此操作,则需要将其写入MemoryStream
,然后在最后调用ms.ToArray()
。问题是:额外的(不必要的)byte[]
分配。这是否有问题取决于您。
是的,你当然是对的。循环是必需的,因为数据可能比 int.MaxValue 长。感谢您的帮助 - 已接受。
@Martyn 不,你误解了我的意思。循环是必需的,因为仅返回 10 个字节是完全合法的,即使基础数据有 100,000 并且缓冲区有 1000 的空间。您必须循环,因为 是知道何时到达末尾的唯一方法是继续阅读【参考方案2】:
你可能会这样做。在 MSDN 上找到。也许它可以达到你的目的
// Reset the starting byte for the new BLOB.
startIndex = 0;
// Read the bytes into outbyte[] and retain the number of bytes returned.
retval = myReader.GetBytes(1, startIndex, outbyte, 0, bufferSize);
// Continue reading and writing while there are bytes beyond the size of the buffer.
while (retval == bufferSize)
bw.Write(outbyte);
bw.Flush();
// Reposition the start index to the end of the last buffer and fill the buffer.
startIndex += bufferSize;
retval = myReader.GetBytes(1, startIndex, outbyte, 0, bufferSize);
// Write the remaining buffer.
bw.Write(outbyte, 0, (int)retval - 1);
bw.Flush();
http://msdn.microsoft.com/en-us/library/87z0hy49%28v=vs.71%29.aspx#Y132
Or this one
int ndx = rdr.GetOrdinal("<ColumnName>");
if(!rdr.IsDBNull(ndx))
long size = rdr.GetBytes(ndx, 0, null, 0, 0); //get the length of data
byte[] values = new byte[size];
int bufferSize = 1024;
long bytesRead = 0;
int curPos = 0;
while (bytesRead < size)
bytesRead += rdr.GetBytes(ndx, curPos, values, curPos, bufferSize);
curPos += bufferSize;
【讨论】:
让我猜猜 - 你是直接从方法的文档中得到的吗? RTFM 问题 ;) 是的 :) 信任 MSDN 到核心。 !! 谢谢,我以前在 MSDN 上看过这个,虽然它演示了使用这种方法检索数据的明显方法,但它没有提供任何背景信息或任何关于正确用法的提示针对特定场景。 感谢第二个例子。当 reader.GetValue 返回一个字符串时,使用它从 SQLite 中提取一个 GIF blob。以上是关于我应该将啥“长度”参数传递给 SqlDataReader.GetBytes()的主要内容,如果未能解决你的问题,请参考以下文章
导入 tkinter 文件时,我应该将啥参数传递给“root”?
我应该将啥对象传递给需要 Void 的函数!在参数中? (科特林)
我应该如何将多个参数传递给 ASP.Net Web API GET?