2012-06-21 32 views
16

Tôi có một SqlDataReader và cần đọc một cột varbinary (max) từ nó bằng phương thức SqlDataReader.GetBytes(). Phương thức này điền một mảng byte và do đó cần biết chiều dài của dữ liệu cần đọc.Tham số 'length' nào tôi nên chuyển tới SqlDataReader.GetBytes()

Đây là nơi tôi bị lẫn lộn .. Rõ ràng là tôi muốn đọc tất cả dữ liệu đã được trả về từ cơ sở dữ liệu trong hàng/cột này vì vậy thông số 'length' nào tôi nên vượt qua? Theo như tôi thấy, SqlDataReader không cung cấp bất kỳ phương pháp nào để khám phá chiều dài của dữ liệu có sẵn, do đó phương pháp này có vẻ hơi khó xử với tôi.

Tôi bị cám dỗ chỉ cần vượt qua int.MaxValue ở đây và quên đi vấn đề nhưng một cái gì đó về điều này không ngồi ngay với tôi.

Tôi biết rằng tôi thay vì có thể gọi

byte[] value = (byte[])dataReader["columnName"]; 

.. và điều này dường như hoàn toàn chăm sóc của vấn đề chiều dài trong nội bộ. Tuy nhiên tôi đang làm việc với một tập các mẫu tạo mã phức tạp đã được xây dựng xung quanh các phương thức SqlDataReader.GetXXXX(). Vì vậy, tôi được gắn vào sử dụng GetBytes và cần phải hiểu cách sử dụng thích hợp của nó.

+0

SQL có các phương thức để yêu cầu độ dài trong một varbinary - bạn phải đặt nó vào SQL: http://msdn.microsoft.com/en-us/library/ms173486.aspx - DATALENGTH. Chọn Field, DataLength (FIeld) từ Table và bạn có thể thấy độ dài. – TomTom

+0

Cảm ơn, tôi đã xem xét việc sử dụng DATALENGTH trong SQL nhưng nó không thực sự là một giải pháp có thể chấp nhận được. Như bạn có thể đoán tất cả các hình thức này là một phần của lớp truy cập dữ liệu, do đó việc ép buộc các quy ước như vậy trong thủ tục được lưu trữ bằng văn bản để hỗ trợ chức năng cơ bản là không lớn. – Martyn

+0

@Martyn khi tôi ở đây ... có thư viện * chuyên dụng * để đảm bảo bạn không cần phải viết mã ADO.NET tẻ nhạt ... "dapper" là (theo ý kiến ​​thiên vị của tôi) tuyệt vời để ẩn đi những mối quan tâm này, trong khi vẫn còn rất nhẹ và không bị xâm nhập. –

Trả lời

26

Khi giao dịch với varbinary(max), có hai kịch bản:

  • chiều dài của dữ liệu được vừa
  • chiều dài của dữ liệu là lớn

GetBytes() dành cho kịch bản thứ hai, khi bạn đang sử dụng CommandBehaviour.SequentialAccess để đảm bảo rằng bạn đang luồng dữ liệu, không đệm nó. Đặc biệt, trong cách sử dụng này, bạn thường sẽ viết (ví dụ) trong một luồng, trong một vòng lặp. Ví dụ:

// 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 
} 

Tuy nhiên! Nếu bạn đang sử dụng dữ liệu có kích thước vừa phải thì mã ban đầu của bạn:

byte[] data = (byte[])reader[col]; 

là tốt !!. Không có gì sai với cách tiếp cận này và trên thực tế, Get* API bị hỏng trong một vài trường hợp - GetChar() là một ví dụ đáng chú ý (gợi ý: nó không hoạt động).

không quan trọng rằng bạn có mã hiện có sử dụng Get* - trong trường hợp này, phương pháp truyền là hoàn toàn phù hợp.

+1

Cảm ơn, đây là một câu trả lời tuyệt vời! Trong trường hợp cụ thể của tôi, toàn bộ dữ liệu sẽ luôn được đọc vào một mảng byte và không được đệm hoặc được đẩy vào luồng khác. Trong trường hợp này, có bất kỳ lợi thế hiệu suất nào để viết vòng lặp đọc theo cách thủ công như trái ngược với việc chỉ gọi GetBytes một lần và chuyển int.MaxValue thành độ dài không? – Martyn

+2

@Martyn 'ReadBytes' là API truyền trực tuyến; bạn có thể ** không bao giờ ** chuyển một giá trị lớn vào đó và giả sử nó đọc mọi thứ - bạn ** luôn luôn ** cần lặp lại cho đến khi bạn lấy lại một giá trị không dương. Đoán chính của tôi ở đây (không được kiểm tra) là * nội bộ * nó đã biết chiều dài (ít nhất, trong việc sử dụng bộ đệm), và sẽ phân bổ một mảng có kích thước phù hợp và chỉ khối-sao chép dữ liệu. Nếu bạn làm điều đó thông qua 'GetBytes()', bạn sẽ cần phải ghi nó vào một 'MemoryStream' và sau đó gọi' ms.ToArray() 'ở cuối. Vấn đề là: bổ sung (không cần thiết) 'byte []' phân bổ. Tùy thuộc vào bạn cho dù đó là một vấn đề. –

+0

Vâng, tất nhiên bạn đã đúng. Vòng lặp là bắt buộc vì dữ liệu có thể dài hơn int.MaxValue. Cảm ơn sự giúp đỡ của bạn - được chấp nhận. – Martyn

1

Bạn có thể làm điều này. Tìm thấy trên MSDN. Có lẽ nó có thể máy chủ mục đích của bạn

// 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; 
      } 
      } 
+0

Điều đó về cơ bản là nó. – TomTom

+1

Hãy để tôi đoán - bạn đã lấy trực tiếp từ tài liệu của phương pháp? Câu hỏi RTFM;) – TomTom

+0

Có :) Tin tưởng MSDN là cốt lõi. !! – Anand

Các vấn đề liên quan