此代码从SQLite反序列化对象。我从DBinaryData(BLOB)字段获取序列化对象。但是获取System.Runtime.Serialization.SerializationException:在解析完成之前遇到的流结束。如何解决这个问题?
public void Dump()
{
try
{
const string databaseName = @"C:\Code\C#\WcfService\WcfService\mainDB.db3";
SQLiteConnection connection = new SQLiteConnection(string.Format("Data Source={0};", databaseName));
connection.Open();
try
{
SQLiteCommand command = new SQLiteCommand("INSERT into 'dump' ('DTime', 'DBinaryData') VALUES ('" + DateTime.Now.ToString() + "', '" + GetSerializedMessages() + "')", connection);
command.ExecuteNonQuery();
}
finally
{
connection.Close();
}
}
catch (Exception e)
{
Logger.Log(e.Message);
}
}
public void Restore()
{
try
{
const string databaseName = @"C:\Code\C#\WcfService\WcfService\mainDB.db3";
SQLiteConnection connection = new SQLiteConnection(string.Format("Data Source={0};", databaseName));
connection.Open();
try
{
SQLiteCommand command = new SQLiteCommand("SELECT * FROM dump ORDER BY DId DESC limit 1", connection);
SQLiteDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Queue<Message> deserializedData = GetDeserializedMessages((byte[])reader["DBinaryData"]);
var data = MergeQueueMessage(deserializedData);
Logger.Log(data.ToString());
}
}
finally
{
connection.Close();
}
}
catch (Exception e)
{
Logger.Log(e.Message);
}
}
public byte[] GetSerializedMessages()
{
byte[] result = null;
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
try
{
lock (MessageQueue.Instance.Messages)
{
formatter.Serialize(memoryStream, MessageQueue.Instance.Messages);
}
result = new byte[memoryStream.GetBuffer().Length];
memoryStream.GetBuffer().CopyTo(result, 0);
}
catch (SerializationException e)
{
Logger.Log("Failed to serialize. Reason: " + e.Message);
}
finally
{
memoryStream.Close();
}
return result;
}
public Queue<Message> GetDeserializedMessages(byte[] source)
{
Queue<Message> messages = null;
using (MemoryStream memoryStream = new MemoryStream(source))
{
BinaryFormatter formatter = new BinaryFormatter();
messages = (Queue<Message>)formatter.Deserialize(memoryStream);
}
return messages;
}
private IEnumerable<Message> MergeQueueMessage(Queue<Message> source)
{
IEnumerable<Message> result = MessageQueue.Instance.Messages.Union(source, new EqualityComparator());
return result;
}
答案 0 :(得分:4)
使用您的编辑:这是一个错误(不确定它是否是“臭虫”):
result = new byte[memoryStream.GetBuffer().Length];
memoryStream.GetBuffer().CopyTo(result, 0);
缓冲区的长度无关紧要。如果重要memoryStream.Length
。坦率地说,这应该只是result = memoryStream.ToArray();
- 这会给你正确的结果。
SQL中的另一个错误:
SQLiteCommand command = new SQLiteCommand("INSERT into 'dump' ('DTime', 'DBinaryData') VALUES ('" + DateTime.Now.ToString() + "', '" + GetSerializedMessages() + "')", connection);
command.ExecuteNonQuery();
连接永远不是一个好主意,但在这里它是致命的;因为GetSerializedMessages()
返回null
(失败 - 不是一个好主意;应该刚刚抛出)或byte[]
,这会做简单的连接。如果连接byte[]
输出不符合您的预期:
byte[] b = {1,2,3};
string s = "a " + b + " c";
// gives: "a System.Byte[] c"
明确不包含您想要的实际数据,因此是胡言乱语。理想情况下,您应该在此处使用参数来处理数据和日期:
SQLiteCommand command = new SQLiteCommand("INSERT into 'dump' ('DTime', 'DBinaryData') VALUES (@when, @data)", connection);
// note: not sure if SQLiteCommand has an "AddWithValue", but the overall usage
// should be something like this
command.Parameters.AddWithValue("when", DateTime.Now);
command.Parameters.AddWithValue("data", GetSerializedMessages());
command.ExecuteNonQuery();
最后:不要吞下问题;您的序列化代码应该更像(IMO)
public byte[] GetSerializedMessages()
{
try {
using(MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
// skipped: serialize etc
return memoryStream.ToArray();
}
} catch(Exception ex) {
Logger.Log("Failed to serialize. Reason: " + ex.Message);
throw; // it doesn't stop being a problem just because we logged it
}
}
首先要看的是byte[]
你 out (通过reader["DBinaryData"]
)是否与你所拥有的byte[]
完全相同你最初序列化了。如果您没有对此进行测试,则所有投注都将被取消。从错误中,听起来它们并不相同 - 这可能是因为:
前两个是完全致命的:如果是那些 - 数据是吐司。
在集成测试中比较两个byte[]
的一种懒惰方法是比较十六进制:
// here expected should be the raw data just after serializing; actual should
// be what you get after storing it in the db and fetching it back, using
// your code
Assert.AreEqual(BitConverter.ToString(expected), BitConverter.ToString(actual));
给出任何delta的十六进制输出。您没有显示序列化和存储消息的方式,因此我无法告诉您是否存在任何明显问题,但请参阅http://marcgravell.blogspot.com/2013/02/how-many-ways-can-you-mess-up-io.html以获取常见问题列表。
最后,我强烈建议: 停止使用BinaryFormatter
。请参阅questions like this以了解其他人的痛苦:基本上,即使是微小的更改(有时只是重建),他们也无法恢复数据。基于合同的序列化器会更加安全 - 我倾向于使用protobuf-net,但我非常偏颇。