Проблема с кодировкой при получении большого двоичного объекта из SQL Server с Entity Framework - PullRequest
4 голосов
/ 28 февраля 2011

Я заметил особенность при попытке сохранить документы MS Word 2003 в поле большого двоичного объекта (nvarchar(max)) SQL Server и затем извлечь их с помощью Entity Framework (EF1), чтобы затем преобразовать их обратно в файл ( прикреплен к письму).

Основной код:

1) Я вставляю документ в базу данных, используя openrowset:

INSERT INTO [dbo].[Attachment]([id],[Blob])     
   SELECT 1, (SELECT BulkColumn FROM OPENROWSET(Bulk 'path_to_attachment\abc123.doc', SINGLE_BLOB) AS BLOB) 

2) Затем я извлекаю файл из базы данных, используя EF1 (упрощено для краткости - в обход репо и т. Д.):

var attachment = (from a in ctx.Attachment where a.id == 1 select a).FirstOrDefault()
var bytes = Encoding.Unicode.GetBytes(attachment.Blob);
var stream = new MemoryStream(bytes);
var fileName = "abc123.doc";
var fileToAttach = new Attachment(stream, fileName, MediaTypeNames.Application.Octet);

Проблема:

Это работает, но я заметил некоторые расхождения с файлом после проталкивания файла через этот процесс, некоторые незначительные проблемы с форматированием и т. Д.

После некоторого углубленного копания (с использованием VBinDiff) - похоже, что некоторые символы Unicode конвертируются в FDFF

00DC > FDFF

оригинал:

00 00 00 00 00 00 00 00 00 DC 00 00 00 00 00 00

конвертирована:

00 00 00 00 00 00 00 00 FD FF 00 00 00 00 00 00

другой пример:

BED9 > FDFF
CFD9 > FDFF

, который выглядит последним в диапазоне отсюда: http://www.unicode.org/charts/PDF/UFB50.pdf

Вопросы

1) Я делаю что-то глупое или EF делает что-то напуганное, возвращая строковый объект attachment.Blob - перед тем, как попытаться преобразовать его обратно в байтовый массив?

2) Есть ли лучший способ извлечь точные байты из поля большого двоичного объекта, все еще используя структуру сущностей? (или я должен использовать хранимую процедуру, или SqlDataReader вместо этого - что я действительно не хочу делать, чтобы не запутать мои пути доступа к данным)

Ответы [ 2 ]

2 голосов
/ 08 марта 2011

Ответ Митча помог указать на ошибку в моем коде. По какой-то причине (я думаю, из-за этой привычки) я установил для поля Blob значение nvarchar(max), когда, как указал Митч, SINGLE_BLOB сохраняет информацию о файле как varbinary(max), что на самом деле и было то, что я хотел в в любом случае (см. пункт 2 вопроса).

Решение:

  1. Преобразовать поле базы данных из nvarchar(max) в varbinary(max)
  2. Обновление модели Entity Framework
  3. Изменить поле BLOB-объекта внутри модели EF с string на двоичное`

И наконец, измените это

var attachment = (from a in ctx.Attachment where a.id == 1 select a).FirstOrDefault()
var bytes = Encoding.Unicode.GetBytes(attachment.Blob);
var stream = new MemoryStream(bytes);
var fileName = "abc123.doc";
var fileToAttach = new Attachment(stream, fileName, MediaTypeNames.Application.Octet);

На это:

var attachment = (from a in ctx.Attachment where a.id == 1 select a).FirstOrDefault()
var stream = new MemoryStream(attachment.Blob);
var fileName = "abc123.doc";
var fileToAttach = new Attachment(stream, fileName, MediaTypeNames.Application.Octet);
1 голос
/ 06 марта 2011

Импорт с использованием SINGLE_BLOB возвращает содержимое файла в виде набора строк из одной строки, состоящего из одного столбца, типа varbinary(max).

Вместо SINGLE_BLOB я бы предложил использовать SINGLE_NCLOB, который читает файл как nvarchar(max).

Файлы Unicode должны читаться с параметром SINGLE_NCLOB, показанным здесь:

SELECT BulkColumn 
FROM OPENROWSET (BULK 'path_to_attachment\abc123.doc', SINGLE_NCLOB) AS BLOB

Ссылка: Использование OPENROWSET для чтения больших файлов в SQL Server

Обновление (в ответ на комментарий): если файлы неUnicode (как вы пытались), то при получении вы не должны использовать кодировку Unicode для получения байтов:

var bytes = Encoding.ASCII.GetBytes(attachment.Blob);
...