2013-04-12 73 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,但我非常偏向。