Reputation: 3437
I'm trying to store a List as a BLOB in a sqlite database, but I'm having some trouble reconstructing the List. I'll admit that I dont know much about how the BinaryFormatter or MemoryStream works.
Inserting a row seems to work fine:
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();
}
The Deserialize method throws a System.Runtime.Serialization.SerializationException: End of Stream encountered before parsing was completed:
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>;
}
}
}
What am I doing wrong here? Also, why does the 5 floats in myList result in 237 bytes when serialized?
Edit: Added the GetBytes function:
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();
}
}
Upvotes: 0
Views: 305
Reputation: 3037
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.
Upvotes: -1
Reputation: 391724
You have hardcoded the size of your serialized data. Don't do that!
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);
This outputs 238, not 237. You've lost one byte which is why you're getting "End of Stream encountered before parsing was completed".
So don't hardcode the size, use all the bytes of the stream. This goes for deserialization as well. And no, this does not mean you should be using 238, hardcoding the expected size is 100% wrong
This code works:
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);
and outputs:
238
12
13
14
15
16
Note that you can construct a MemoryStream
around an existing byte array and then you don't have to reposition it so the deserialization from my example above can be shortened to:
var stream2 = new MemoryStream(array);
foreach (var value in formatter.Deserialize(stream2) as List<float>)
Console.WriteLine(value);
Upvotes: 5