2013-04-12 5 views
0

Ce code désérialise un objet de SQLite. Je reçois un objet sérialisé du champ DBinaryData (BLOB). Mais obtenez System.Runtime.Serialization.SerializationException: fin du flux rencontré avant l'analyse. Comment régler ceci?SerializationException lors de la désérialisation

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

Édité ma réponse à votre modification - il y a certainement un bug dans votre code de sérialisation. –

+0

Il y a aussi un bug majeur dans votre SQL –

Répondre

4

Avec votre édition: voici un bug (pas sûr si elle est "le" bug, bien):

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

La longueur de la mémoire tampon est hors de propos. Si c'est le memoryStream.Length qui compte. Franchement, cela devrait simplement être result = memoryStream.ToArray(); - ce qui vous donnerait le résultat correct.


Et un autre bogue dans le SQL:

SQLiteCommand command = new SQLiteCommand("INSERT into 'dump' ('DTime', 'DBinaryData') VALUES ('" + DateTime.Now.ToString() + "', '" + GetSerializedMessages() + "')", connection); 
command.ExecuteNonQuery(); 

est Enchaînement jamais une bonne idée, mais ici elle est fatale; puisque GetSerializedMessages() renvoie null (en cas d'échec - pas une bonne idée, aurait dû être levée) ou un byte[], cela fait une concaténation simple. Si vous concaténer une byte[] la sortie n'est pas ce que vous attendez:

byte[] b = {1,2,3}; 
string s = "a " + b + " c"; 
// gives: "a System.Byte[] c" 

qui clairement ne contient pas les données réelles que vous vouliez, est donc charabia. Idéalement, vous devriez utiliser les paramètres ici pour les données et la 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(); 

Enfin: ne pas avaler les problèmes; votre code de sérialisation devrait être (OMI) plus comme

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 
    } 
} 

La première chose à regarder est de savoir si le byte[] vous obtenez sur (via reader["DBinaryData"]), est 100% identique à la byte[] que vous aviez lorsque vous à l'origine sérialisé. Si vous n'avez pas de test pour cela, tous les paris sont désactivés. De l'erreur, on dirait qu'ils ne sont pas identiques - cela pourrait être dû à:

  • une erreur dans le code qui sérialise et stocke les données
  • troncature à l'intérieur du stockage de base de données
  • de troncature
  • lors de la lecture le blob (certaines connexions limiter le montant tiré par les cheveux en une seule fois)
  • une erreur dans le code qui va chercher et désérialise les données

les deux premiers sont totalement fatale: si ce sont ceux - les données sont t oast.

Une façon paresseuse pour comparer deux byte[] dans un test d'intégration est de comparer l'hexagone:

// 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)); 

qui donne une belle sortie hexagonale de tout delta.Vous ne montrez pas comment vous sérialisez et stockez les messages, donc je ne peux pas vous dire s'il y a des problèmes évidents là, mais veuillez voir http://marcgravell.blogspot.com/2013/02/how-many-ways-can-you-mess-up-io.html pour une liste de problèmes communs ici.

Enfin, je vous conseille fortement: cesser d'utiliser BinaryFormatter pour cela. Voir questions like this pour voir la douleur des autres: fondamentalement, ils ne peuvent pas récupérer leurs données après des changements même mineures (ou parfois juste reconstruit). Les sérialiseurs basés sur les contrats seraient beaucoup plus sûrs - je penche pour protobuf-net, mais je suis extrêmement partial.

Questions connexes