2013-04-12 31 views
0

此代码从SQLite反序列化对象。我从DBinaryData(BLOB)字段获取序列化对象。但是得到System.Runtime.Serialization.SerializationException:分析完成之前遇到的流结束。如何解决这个问题?反序列化时的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

编辑我的答案重新编辑 - 你的序列化代码中肯定存在一个错误。 –

+0

你的SQL中还有一个主要的bug –

回答

4

随着您的编辑:这里是一个错误(如果不知道它是“”虫,虽然):

result = new byte[memoryStream.GetBuffer().Length]; 
memoryStream.GetBuffer().CopyTo(result, 0); 

缓冲区的长度是无关紧要的。如果是重要的memoryStream.Length。坦白说,这应该只是result = memoryStream.ToArray(); - 这会给你正确的结果。


和SQL另一个bug:

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[](通过reader["DBinaryData"]),是100%相同的byte[]你有当你最初是连载的。如果你没有测试,所有投注都关闭。从错误中,这听起来像他们不是相同的 - 这可能是因为:读取时

  • 在序列化和存储数据
  • 截断数据库存储
  • 截断内部代码中的错误将BLOB(一些连接限制一气呵成取出的量)
  • 在其获取和反序列化数据

前两个是完全致命代码中的错误:如果它是那些 - 所述数据是叔oast。

懒惰的方式在集成测试来比较两个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)); 

这给任何三角洲的一个不错的十六进制输出。您不会显示如何序列化和存储消息,因此我无法告诉您是否存在任何明显的问题,但请参阅http://marcgravell.blogspot.com/2013/02/how-many-ways-can-you-mess-up-io.html以获取此处常见问题的列表。

最后,我强烈建议:停止使用BinaryFormatter这一点。看到questions like this看到其他人的痛苦:基本上他们不能得到他们的数据后,即使微小的变化(或有时只是重建)回来。基于合约的序列化器会更安全 - 我倾向于protobuf-net,但我非常偏向。