Я пытаюсь сохранить список как большой двоичный объект в базе данных sqlite, но у меня возникли проблемы с восстановлением списка. Признаюсь, я мало знаю о том, как работает BinaryFormatter или MemoryStream.
Кажется, что вставка строки работает нормально:
public void InsertRow()
{
List<float> myList = new List<float>();
myList.Add(12);
myList.Add(13);
myList.Add(14);
myList.Add(15);
myList.Add(16);
var binFormatter = new BinaryFormatter();
var mStream = new MemoryStream();
binFormatter.Serialize(mStream, myList);
SQLiteCommand command = new SQLiteCommand(m_dbConnection);
byte[] data = mStream.ToArray();
command.CommandText = "insert into photos (photo) values (@photo)";
command.Parameters.Add("@photo", DbType.Binary, 237).Value = data;
command.ExecuteNonQuery();
}
Метод Deserialize генерирует исключение System.Runtime.Serialization.SerializationException: конец потока, обнаруженный до завершения синтаксического анализа:
public void GetRow()
{
string sql = "select photo from photos where id = 1";
SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
byte[] buffer = GetBytes(reader);
var mStream = new MemoryStream();
var binFormatter = new BinaryFormatter();
mStream.Write(buffer, 0, 237);
mStream.Position = 0;
var myObject = binFormatter.Deserialize(mStream) as List<float>;
}
}
}
Что я здесь делаю не так? Кроме того, почему 5 чисел с плавающей запятой в myList приводят к 237 байтам при сериализации?
Обновлено: добавлена функция GetBytes:
static byte[] GetBytes(SQLiteDataReader reader)
{
const int CHUNK_SIZE = 2 * 1024;
byte[] buffer = new byte[CHUNK_SIZE];
long bytesRead;
long fieldOffset = 0;
using (MemoryStream stream = new MemoryStream())
{
while ((bytesRead = reader.GetBytes(0, fieldOffset, buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, (int)bytesRead);
fieldOffset += bytesRead;
}
return stream.ToArray();
}
}
На моей машине ваш код сериализует 238 байтов данных, не кодируйте размер жестко, используйте mStream.Length.





binFormatter.Serialize(mStream, myList);
mStream.Close(); // to be sure
byte[] data = mStream.ToArray();
command.Parameters.Add("@photo", DbType.Binary).Value = data; // don't specify (override) the size.
Как это вообще ответ ?. без объяснения, OP говорит, что он столкнулся с исключением во время десериализации.
И исключения в десериализации часто вызваны ошибками во время сериализации.
хорошо, как закрытие MemoryStream во время сериализации связано с исключением во время десериализации ?.
Никогда не вызывайте ToArray в открытом потоке.
Вы жестко указали размер сериализованных данных. Не делай этого!
List<float> myList = new List<float>();
myList.Add(12);
myList.Add(13);
myList.Add(14);
myList.Add(15);
myList.Add(16);
var binFormatter = new BinaryFormatter();
var mStream = new MemoryStream();
binFormatter.Serialize(mStream, myList);
Console.WriteLine(mStream.Length);
Это выводит 238, а не 237. Вы потеряли один байт, поэтому вы получаете сообщение «Конец потока обнаружен до завершения анализа».
Так что не кодируйте размер жестко, используйте все байты потока. Это также касается десериализации. И нет, это не означает, что вы должны использовать 238, жесткое кодирование ожидаемого размера на 100% неверно
Этот код работает:
List<float> myList = new List<float>();
myList.Add(12);
myList.Add(13);
myList.Add(14);
myList.Add(15);
myList.Add(16);
var formatter = new BinaryFormatter();
var stream1 = new MemoryStream();
formatter.Serialize(stream1, myList);
var array = stream1.ToArray();
Console.WriteLine(array.Length);
var stream2 = new MemoryStream();
stream2.Write(array, 0, array.Length);
stream2.Position = 0;
foreach (var value in formatter.Deserialize(stream2) as List<float>)
Console.WriteLine(value);
и выходы:
238
12
13
14
15
16
Обратите внимание, что вы можете построить MemoryStream вокруг существующего байтового массива, и тогда вам не нужно его перемещать, поэтому десериализацию из моего примера выше можно сократить до:
var stream2 = new MemoryStream(array);
foreach (var value in formatter.Deserialize(stream2) as List<float>)
Console.WriteLine(value);
Спасибо! Не могу поверить, что я не уловил это из-за одной ошибки ... Я на 100% согласен с тем, что жесткое кодирование размера неверно, это было просто во время экспериментов. Еще раз спасибо!
Обратите внимание: вам следует наверное пересмотреть решение об использовании BinaryFormatter и сохранении объектов в базе данных. Проблемы с версией со временем могут сделать старые данные невозможными или очень трудными для десериализации. Вам следует подумать об использовании формата, который не так тесно связан с реальными объектами, как BinaryFormatter, например json, xml или protobuf. См. docs.microsoft.com/en-us/dotnet/standard/serialization/… для получения некоторой информации по этому поводу, но есть много других статей по этой теме.
Я согласен, я решил вместо этого преобразовать List <float> в array [], сохранив только необработанные данные.
Почему вы жестко задаете размер данных? Что делает метод
GetBytes()? Что касается почему + размер: сериализованные данные содержат много информации о метаданных. Вы можете убедиться в этом сами, если сохраните данные в виде файла и откроете его в текстовом редакторе.