问题描述
此代码反序列化来自SQLite的对象.我从DBinaryData(BLOB)字段获取序列化的对象.但是获取System.Runtime.Serialization.SerializationException:在解析完成之前遇到流的结尾.该如何解决?
This code deserialize object from SQLite. I'm get serialized object from DBinaryData (BLOB) field. But get System.Runtime.Serialization.SerializationException: end of stream encountered before parsing was completed. How to fix this?
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; }
推荐答案
通过您的这是一个错误(尽管不确定是否是"the"错误):
With your edit: here's a bug (not sure if it is "the" bug, though):
result = new byte[memoryStream.GetBuffer().Length]; memoryStream.GetBuffer().CopyTo(result, 0);
缓冲区的长度无关紧要. memoryStream.Length是否重要.坦白地说,这应该是result = memoryStream.ToArray();-这将为您提供正确的结果.
The length of the buffer is irrelevant. If is the memoryStream.Length that matters. Frankly, this should just be result = memoryStream.ToArray(); - which would give you the correct result.
SQL中的另一个错误:
And another bug in the SQL:
SQLiteCommand command = new SQLiteCommand("INSERT into 'dump' ('DTime', 'DBinaryData') VALUES ('" + DateTime.Now.ToString() + "', '" + GetSerializedMessages() + "')", connection); command.ExecuteNonQuery();
串联从来都不是一个好主意,但在这里却是致命的.因为GetSerializedMessages()返回null(失败时-不是一个好主意;应该抛出)或byte[],所以这样做很简单.如果连接byte[],则输出将不是您期望的:
Concatenation is never a good idea, but here it is fatal; since GetSerializedMessages() returns either null (on failure - not a good idea; should have just thrown) or a byte[], this does simple concatenation. If you concatenate a byte[] the output is not what you expect:
byte[] b = {1,2,3}; string s = "a " + b + " c"; // gives: "a System.Byte[] c"
很明显不包含您想要的实际数据,所以很乱.理想情况下,您应该在此处为数据和日期使用参数:
that clearly doesn't contain the actual data you wanted, so is gibberish. Ideally you should be using parameters here for both the data and the date:
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)
Finally: don't swallow problems; your serialization code should be (IMO) more like
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 } }
首先要看的是(通过reader["DBinaryData"])通过退出的byte[]是否与最初序列化时的byte[] 100%相同.如果您没有为此进行测试,那么所有的选择都将落空.从错误中得知,它们似乎并不相同-这可能是由于:
The first thing to look at is whether the byte[] you get out (via reader["DBinaryData"]), is 100% identical to the byte[] you had when you originally serialized. If you don't have a test for that, all bets are off. From the error, it sounds like they're not identical - this could be because of:
- 序列化和存储数据的代码中的错误
- 数据库存储内部的截断
- 读取BLOB时出现截断(某些连接限制了一次获取的数量)
- 获取和反序列化数据的代码错误
前两个完全是致命的:如果是致命的-数据就是吐司.
The first two are totally fatal: if it is those - the data is toast.
在集成测试中比较两个byte[]的一种惰性方法是比较十六进制:
A lazy way to compare two byte[] in an integration test is to compare the hex:
// 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));
给出了任意增量的十六进制输出.您没有显示如何序列化和存储消息,所以我不能告诉您那里是否存在任何明显的问题,但是请参阅 http://marcgravell.blogspot.com/2013/02/how-many-ways-can-you-mess -up-io.html 以获得此处的常见问题列表.
which gives a nice hex output of any delta. You don't show how you serialize and store the messages, so I can't tell you whether there are any obvious issues there, but please see http://marcgravell.blogspot.com/2013/02/how-many-ways-can-you-mess-up-io.html for a list of common issues here.
最后,我强烈建议: 为此停止使用BinaryFormatter .请参阅类似的问题,以了解其他人的痛苦:基本上,他们不能在以后恢复数据甚至很小的更改(或有时只是重建).基于合同的序列化器会更安全-我倾向于protobuf-net,但我有很大的偏见.
Finally, I strongly advise: stop using BinaryFormatter for this. See questions like this to see other people's pain: basically they can't get their data back after even minor changes (or sometimes just rebuilds). Contract-based serializers would be much safer - I lean towards protobuf-net, but I'm hugely biased.
这篇关于反序列化时出现SerializationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!