Итак, я пытаюсь использовать ADO.NET для потоковой передачи данных файла, хранящихся в столбце изображения в базе данных SQL Compact.
Для этого я написал класс DataReaderStream, который принимает считыватель данных, открывается для последовательного доступа и представляет его как поток, перенаправляя вызовы Read (...) в потоке в IDataReader.GetBytes (... ).
Один «странный» аспект IDataReader.GetBytes (...) по сравнению с классом Stream заключается в том, что GetBytes требует, чтобы клиент увеличивал смещение и передавал его при каждом вызове. Он делает это, даже если доступ является последовательным, и невозможно прочитать «назад» в потоке устройства чтения данных.
Реализация IDataReader в SqlCeDataReader усиливает это, увеличивая внутренний счетчик, который определяет общее количество возвращенных байтов. Если вы передадите число, которое меньше или больше этого числа, метод сгенерирует исключение InvalidOperationException.
Проблема с этим, однако, заключается в том, что в реализации SqlCeDataReader есть ошибка, из-за которой для внутреннего счетчика устанавливается неправильное значение. Это приводит к тому, что последующие вызовы Read в моем потоке выдают исключения, когда их не должно быть.
Я нашел некоторую информацию об ошибке в этой ветке MSDN .
Мне удалось найти отвратительный, ужасно хакерский обходной путь, который в основном использует отражение, чтобы обновить поле в классе до правильного значения.
Код выглядит так:
public override int Read(byte[] buffer, int offset, int count)
{
m_length = m_length ?? m_dr.GetBytes(0, 0, null, offset, count);
if (m_fieldOffSet < m_length)
{
var bytesRead = m_dr.GetBytes(0, m_fieldOffSet, buffer, offset, count);
m_fieldOffSet += bytesRead;
if (m_dr is SqlCeDataReader)
{
//BEGIN HACK
//This is a horrible HACK.
m_field = m_field ?? typeof (SqlCeDataReader).GetField("sequentialUnitsRead", BindingFlags.NonPublic | BindingFlags.Instance);
var length = (long)(m_field.GetValue(m_dr));
if (length != m_fieldOffSet)
{
m_field.SetValue(m_dr, m_fieldOffSet);
}
//END HACK
}
return (int) bytesRead;
}
else
{
return 0;
}
}
По понятным причинам, я бы предпочел не использовать это.
Однако я не хочу буферизовать все содержимое большого двоичного объекта в памяти.
Кто-нибудь знает, как можно получать потоковые данные из базы данных SQL Compact, не прибегая к такому ужасному коду?