【发布时间】:2016-05-11 07:49:03
【问题描述】:
如果需要以流的方式从 SqlServer 读取数据,那么有一些功能可以做到这一点。比如使用SqlDataReader和CommandBehavior.SequentialAccess,特别是当需要访问二进制列数据时,有GetStream(int)方法:
var cmd = new SqlCommand();
cmd.Connection = connection;
cmd.CommandText = @"select 0x0123456789 as Data";
using (var dr = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
dr.Read();
var stream = dr.GetStream(0);
// access stream
}
但是如果需要使用SqlBulkCopy 向 SqlServer 提供数据到时,特别是如果需要将流作为二进制列的数据源提供时,反方向的流数据呢? ?
我试过了
var cmd2 = new SqlCommand();
cmd2.Connection = connection;
cmd2.CommandText = @"create table #Test (ID int, Data varbinary(max))";
cmd2.ExecuteNonQuery();
using (SqlBulkCopy sbc = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, null))
{
sbc.DestinationTableName = "#Test";
sbc.EnableStreaming = true;
sbc.ColumnMappings.Add(0, "ID");
sbc.ColumnMappings.Add(1, "Data");
sbc.WriteToServer(new TestDataReader());
}
其中TestDataReader 实现IDataReader 如下:
class TestDataReader : IDataReader
{
public int FieldCount { get { return 2; } }
int rowCount = 1;
public bool Read() { return (rowCount++) < 3; }
public bool IsDBNull(int i) { return false; }
public object GetValue(int i)
{
switch (i)
{
case 0: return rowCount;
case 1: return new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89 };
default: throw new Exception();
}
}
//the rest members of IDataReader
}
它按预期工作。
但是变化
case 1: return new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89 };
到
case 1: return new MemoryStream(new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89 });
导致异常System.InvalidOperationException与消息
来自数据源的 MemoryStream 类型的给定值不能 转换为指定目标列的 varbinary 类型。
有没有办法将Stream 从IDataReader(或者可能是DbDataReader)提供到SqlBulkCopy 作为二进制列的数据源,而无需先将其所有数据复制到内存(字节数组)中?
【问题讨论】:
-
您对如何使用自定义 IDataReader 来实现这一点感兴趣,或者实际上您将使用一些现有的数据读取器(如 SqlDataReader)?
-
@Evk,带有自定义
IDataReader或DbDataReader(我正在使用从二进制/xml 文件提供数据的自定义实现,但是像我的问题中的一些虚拟实现就足够了)。跨度> -
我试图检查
SqlBulkCopy和TdsParser的内部结构(在ReferenceSource 上)。看来这应该是可能的。SqlBulkCopy调用DbDataReader.GetStream(int)方法,然后将结果包装到StreamDataFeed,然后传递给TdsParser.WriteStreamFeed()方法。我试图从DbDataReader继承我的数据阅读器,但我无法调用GetStream(int)。似乎我错过了什么。 -
当你写你的评论时,我正在写一个与这个提议完全相同的答案:) 但是我确实测试了它并且它对我来说很好(使用你的示例中的代码)。
标签: c# sql-server ado.net sqlbulkcopy idatareader