我一直在研究一种以流方式提供对大型SQL结果的访问的方法,尤其是当查询使用for json或for xml子句时。
我注意到,当查询返回json或xml时,SqlDataReader
不允许使用GetStream()
方法,根据文档要求:
'对列'c'的GetStream的无效尝试。 GetStream函数只能在Binary,Image,Udt或VarBinary类型的列上使用。'
但是,确实允许使用GetBytes()
。
当返回的数据为varchar(max)
或nvarchar(max)
时,行为相同。 GetStream()
不起作用,但GetBytes()
将起作用。
使用常规的,受长度限制的varchar
或nvarchar
列,不允许GetStream
和GetBytes
。
能够调用GetBytes
意味着我能够相当容易地构建自定义SqlTextStream : Stream
类,但前提是从nvarchar
读取时最好读取多个字节以免将字符撕成两半。
我查看了SqlDataReader.GetColumnSchema()
提供的信息,但没有发现明显的理由为什么对GetBytes
的结果允许使用n/varchar(max)
。我可能已经错过了一些内容,但是对于常规或最大字符数据,GetColumnSchema
的输出似乎与长度相同。
有人知道为什么对GetBytes
列允许使用n/varchar(max)
吗?您认为依靠GetBytes
被允许安全吗?
下面是一些简单的测试代码:
public void Test()
{
var cmd1 = "select c = 'getbytes permitted here' for json path";
var cmd2 = "select c = cast('getbytes also permitted here' as nvarchar(max))";
var cmd3 = "select c = cast('getbytes not permitted here' as nvarchar(32))";
using (var con = new SqlConnection("data source=theDB; initial catalog=playground; integrated security=SSPI"))
// switch between cmd1, cmd2 and cmd3 to see the different behaviour.
using (var cmd = new SqlCommand(cmd1, con))
{
con.Open();
using (var rdr = cmd.ExecuteReader(System.Data.CommandBehavior.SequentialAccess))
{
var o = rdr.GetColumnSchema();
var buffer = new byte[128];
rdr.Read();
//System.IO.Stream s = rdr.GetStream(0); this is never permitted
rdr.GetBytes(0, 0, buffer, 0, buffer.Length); // this is permitted for cmd1 and cmd2
}
}
}
答案 0 :(得分:1)
// don't allow get bytes on non-long or non-binary columns
MetaType mt = _metaData[i].metaType;
if (!(mt.IsLong || mt.IsBinType) || (SqlDbType.Xml == mt.SqlDbType)) {
throw SQL.NonBlobColumn(_metaData[i].column);
}
不执行任何转换;因此,检索到的数据必须已经是字节数组。
FOR JSON
和FOR XML
表现出相同行为的原因应该是它们都返回NVARCHAR(MAX)
,但是做FOR XML, TYPE
会返回 true SqlXml格式的XML。