2016-05-11 20 views
2

SqlServerからストリーム形式で読み取る必要がある場合は、そのための機能がいくつかあります。一つのデータをを供給する必要がある場合、逆方向にデータをストリーミング約SqlBulkCopyが使用されている場合、バイナリ列のデータソースとしてストリームを供給

var cmd = new SqlCommand(); 
cmd.Connection = connection; 
cmd.CommandText = @"select 0xas Data"; 

using (var dr = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) 
{ 
    dr.Read(); 

    var stream = dr.GetStream(0); 
    // access stream 
} 

しかし:そのようなバイナリ列データにアクセスする必要がある場合に特にCommandBehavior.SequentialAccessSqlDataReaderを使用して、としてGetStream(int)その方法があります SqlServer SqlBulkCopyを使用し、特にストリームをバイナリ列のデータソースとして指定する必要がある場合は、私はどこTestDataReader実装IDataReaderとして

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()); 
} 

を、次の試してみました

は次のとおりです。

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 MemoryStream(new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89 }); 

case 1: return new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89 }; 

を変更は がVARBINARY型に変換できないメッセージ

データ・ソースからの型のMemoryStreamの所定の値と例外System.InvalidOperationExceptionを引き起こし指定されたターゲット列の

バイナリ列のデータのソースとしてSqlBulkCopyIDataReader(又はおそらくDbDataReader)からStreamを供給するための方法は、最初のメモリ(バイト配列)にすべてのデータをコピーせずに、ありますか?

+0

参照のWebページ:https://msdn.microsoft.com/en-us/library/system.data。sqlclient.sqlbulkcopy(v = vs.110).aspx – jdweng

+0

カスタムIDataReaderでこれを行うには興味がありますか、実際には既存のデータリーダー(SqlDataReaderなど)を使用しますか? – Evk

+0

@Evk、カスタム 'IDataReader'ま​​たは' DbDataReader'(私はバイナリ/ xmlファイルからデータをフィードするカスタム実装を使用していますが、私の質問のようないくつかのダミーの実装で十分です)。 –

答えて

3

わからないを参照してください。これはどこかに文書化されていますが、SqlBulkCopyソースコードの短い検査を行う場合には、それがさまざまな方法で異なるデータ・リーダを扱うことを見つけることがあります。 まず、SqlBulkCopyはストリーミングとGetStreamをサポートしていますが、IDataReaderインターフェイスにはGetStreamメソッドが含まれていません。したがって、カスタムIDataReaderの実装をSqlBulkCopyにフィードすると、ストリームとしてバイナリ列が処理されず、Streamタイプの値は受け入れられません。これに対して、DbDataReaderになります。 SqlBulkCopyのインスタンスにDbDataReader -inherited classを指定すると、すべてのバイナリ列がストリーム方式で処理され、DbDataReader.GetStreamが呼び出されます。

だからあなたの問題を解決するには - このようDbDataReader継承:

class TestDataReader : DbDataReader 
{ 
    public override bool IsDBNull(int ordinal) { 
     return false; 
    } 

    public override int FieldCount { get; } = 2; 
    int rowCount = 1; 

    public override bool HasRows { get; } = true; 
    public override bool IsClosed { get; } = false; 

    public override bool Read() 
    { 
     return (rowCount++) < 3; 
    } 

    public override object GetValue(int ordinal) { 
     switch (ordinal) { 
      // do not return anything for binary column here - it will not be called 
      case 0: 
       return rowCount; 
      default: 
       throw new Exception(); 
     } 
    } 

    public override Stream GetStream(int ordinal) { 
     // instead - return your stream here 
     if (ordinal == 1) 
      return new MemoryStream(new byte[] {0x01, 0x23, 0x45, 0x67, 0x89}); 
     throw new Exception(); 
    } 
    // bunch of irrelevant stuff 

} 
+0

私は、GetStreamが2回目の読者実装(IDataReaderではなく 'DbDataReader'から継承されたもの)で呼び出されなかった理由を知りました。おそらく、さまざまな方向でコードを再生している間に、私は間違いを導入しました(ある時点で、ターゲット列を 'varbinary(max) 'ではなく' varbinary(100) 'に変更しました)。問題のコードとあなたのコードを組み合わせてもうまくいきました。それは可能ですありがとうございました。 –

0

は、次のコード

static int SendOrders(int totalToSend) 
    { 
     using (SqlConnection con = new SqlConnection(connectionString)) 
     { 
     con.Open(); 
     using (SqlTransaction tran = con.BeginTransaction()) 
     { 
      var newOrders = 
        from i in Enumerable.Range(0, totalToSend) 
        select new Order 
        { 
        customer_name = "Customer " + i % 100, 
        quantity = i % 9, 
        order_id = i, 
        order_entry_date = DateTime.Now 
        }; 

      SqlBulkCopy bc = new SqlBulkCopy(con, 
      SqlBulkCopyOptions.CheckConstraints | 
      SqlBulkCopyOptions.FireTriggers | 
      SqlBulkCopyOptions.KeepNulls, tran); 

      bc.BatchSize = 1000; 
      bc.DestinationTableName = "order_queue"; 
      bc.WriteToServer(newOrders.AsDataReader()); 

      tran.Commit(); 
     } 
     con.Close(); 

     } 

     return totalToSend; 

    } 
+1

申し訳ありませんが、役に立たないようです。私はすでに私の質問で 'SqlBulkCopy'の使用例を持っています。問題はこの使用法の詳細です。特に、すべてのストリームデータをメモリに最初にコピーすることなく、 'IDataReader' +' SqlBulkCopy'を介して 'Stream'データをバイナリ列に供給することが可能です。 –

関連する問題