2010-06-23 8 views
2

En ce moment, je suis confronté à un problème très frustrant. Je vais essayer d'abstraire le problème pour le rendre un peu plus facile. Il doit sérialiser mon objet personnalisé à une base de données dans un processus et le désérialiser dans un autre processus.Désérialiser un MemoryStream - comportement inattendu

J'ai deux assemlies; AppToDB.dll et AppFromDB.dll. J'ai un 3ème assemblage - MyCustomObject.dll - auquel ces deux assemblages contiennent une référence. Le MyCustomObject.dll s'étend MarshalByRefObject.

Dans mon AppToDB.dll j'exécute le code suivant:

public bool serializeToDB(MyCustomObject obj) 
    {    
     OdbcDataAdapter da = new OdbcDataAdapter(); 
     MemoryStream memStream = new MemoryStream(); 

     try 
     { 
      ObjRef marshalledObj = RemotingServices.Marshal((System.MarshalByRefObject)obj); 

      // Serialize the object; construct the desired formatter 
      IFormatter oBFormatter = new BinaryFormatter(); 

      // Try to serialize the object 
      oBFormatter.Serialize(memStream, marshalledObj); 

      // Create byte array 
      byte[] serialized = memStream.ToArray(); 

      // Build the query to write to the database 
      string queryString = 
        "INSERT INTO MyCustomObject(id, object) VALUES(?, ?)"; 
      OdbcCommand command = new OdbcCommand(queryString, connection); 
      command.Parameters.AddWithValue("id", 1); 
      command.Parameters.AddWithValue("object", serialized); 

      // Write the object byte array to the database 
      int num = command.ExecuteNonQuery(); 
    } 
    catch { } 
} 

En AppFromDB.dll j'exécute ce code:

public OCR.Batch deserializeFromDB() 
    {    
     MemoryStream memStream = new MemoryStream(); 

     try 
     { 
      string queryString = "SELECT object FROM FCBatch"; 
      OdbcCommand command = new OdbcCommand(queryString, connection); 
      OdbcDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess); 

      // Size of the BLOB buffer. 
      int bufferSize = 100; 
      // The BLOB byte[] buffer to be filled by GetBytes. 
      byte[] outByte = new byte[bufferSize]; 
      // The bytes returned from GetBytes. 
      long retval; 
      // The starting position in the BLOB output. 
      long startIndex = 0; 

      MemoryStream dbStream = new MemoryStream(); 

      while (reader.Read()) 
      { 
       // Reset the starting byte for the new BLOB. 
       startIndex = 0; 

       // Read bytes into outByte[] and retain the number of bytes returned. 
       retval = reader.GetBytes(0, startIndex, outByte, 0, bufferSize); 

       // Continue while there are bytes beyond the size of the buffer. 
       while (retval == bufferSize) 
       { 
        dbStream.Write(outByte, 0, bufferSize); 
        dbStream.Flush(); 

        // Reposition start index to end of last buffer and fill buffer. 
        startIndex += bufferSize; 
        retval = reader.GetBytes(0, startIndex, outByte, 0, bufferSize); 
       } 

       // Write the remaining buffer. 
       dbStream.Write(outByte, 0, (int)retval); 
       dbStream.Flush(); 
      } 
      // Close the reader and the connection. 
      reader.Close(); 

      dbStream.Position = 0; 
      object temp = oBFormatter.Deserialize(dbStream); 
      MyCustomObject obj = (MyCustomObject)temp; 

      return null; 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message); 
      return null; 
     } 
    } 

OK, dans les deux morceaux de code que vous pouvez voir un objet MemoryStream. Dans le premier AppToDB il est créé et si je regarde son contenu il contient 707 octets. Bien. Je l'écris à la base de données et l'enregistre en tant que BLOB. Maintenant, dans AppFromDB, je récupère le BLOB et le stocke dans un tableau byte[]. J'écris le tableau byte[] à nouveau à MemoryStream, et vois que mes objets MemoryStream contiennent 707 octets, qui sont tous en place comme l'original. Il semble que j'ai transféré l'objet avec succès!

Maintenant, le problème réside avec object temp = oBFormatter.Deserialize(dbStream);. Dès que j'essaye de désérialiser, mon object est un proxy transparent et je suis incapable de lancer le MyCustomObject !! Comment récupérer mon objet d'origine? Comment dans le nom de # @ & puis-je avoir un objet MemoryStream .... en mémoire ... prêt à être sérialisé ... et tout à coup c'est un proxy transparent à nouveau.

Je suis à perte. L'aide est appréciée. Je vais prier # @ & pour celui qui a la réponse;)

Modifier 1 OK, je dois dire que les choses commencent à donner un sens maintenant (même si le problème persiste). Mon problème: j'ai un objet (y compris l'état) d'un côté et j'ai besoin de le stocker dans la base de données pour pouvoir l'utiliser quelques jours plus tard par un autre processus de l'autre côté.

Mon objet n'est pas sérialisable, car il enveloppe un objet tiers qui n'est pas marqué comme sérialisable. Donc, ma seule option semble être marshal, ce qui renvoie un ObjRef, qui à son tour est sérialisable. Mais bien sûr - quelques jours plus tard - l'objet que je désérialise est simplement la référence et mon objet original est parti.

Comment résoudre mon problème? Plus de gens doivent avoir rencontré et je ne peuvent tout simplement pas trouver la réponse ...

Edit 2 OK, je suppose que je vais écrire ma propre classe sérialisable isomorphe à l'objet 3ème partie. Ensuite, parcourez tout l'objet 3ème partie et, stocker/envelopper ses informations d'état, etc. Puis sérialiser mon objet à la base de données ... Semble être ma seule option.

Édition 3 À partir de ce problème à nouveau après un certain temps. Je me suis juste rendu compte que cette solution publiée dans Edit 2 ne fonctionnera pas. Je dois désérialiser dans un objet que l'assemblée de tiers connaît, puisqu'elle continuera à effectuer des opérations dessus.

+0

double possible de [Comment maréchal un objet et son contenu (aussi des objets)] (http://stackoverflow.com/questions/3068702/how-to-marshal-an-object-and-its- contenu-aussi-objets) – Lucero

Répondre

1

Vous numérotez le ObjRef qui n'est pas l'objet d'origine mais une "référence d'accès distant", contenant toutes les informations pour transférer la référence.

De MSDN (mine d'importance):

ObjRef contient des informations qui décrit le type et la classe de l'objet étant marshalés, son emplacement exact et relatives à la communication d'informations sur la façon d'atteindre le subdivision à distance où se trouve l'objet .

Après une classe d'exécution MarshalByRefObject est assemblée, le ObjRef qui le représente est transférée à travers un canal dans un autre domaine d'application, éventuellement dans un autre processus ou un ordinateur. Lorsque ObjRef est désérialisé dans le domaine d'application cible , est analysé pour créer un proxy transparent pour l'objet MBR distant. Cette opération est appelée unmarshaling.

Modifier (en raison de la nouvelle information fournie):

Il existe plusieurs approches possibles pour résoudre votre problème. Ils ont tous leurs propres limites, que je vais mettre ensemble ici:

  • Utilisez sérialisation XML avec le XmlSerializer. La sérialisation XML est différente en ce sens qu'elle sérialise et désérialise toutes les propriétés publiques d'un objet au lieu de sérialiser les champs et/ou les données personnalisées (lorsque ISerializable est implémenté par l'objet à sérialiser). Par conséquent, si les objets tiers ne sont que des conteneurs de données, la sérialisation des propriétés publiques est suffisante et ils fournissent un constructeur par défaut, cela ne pose aucun problème. Créez votre propre code de sérialisation "de bas niveau", en utilisant la réflexion pour obtenir les champs et sérialiser ceux-ci. Cette approche nécessite généralement que votre application s'exécute en toute confiance (la réflexion et la modification des champs privés via la réflexion nécessitent des autorisations qui ne sont généralement pas présentes dans les approbations inférieures). Voici quelques méthodes qui peuvent vous aider: FormatterServices.GetSerializableMembers() pour obtenir les champs à sérialiser, FormatterServices.GetObjectData() pour obtenir les données de champ à partir d'une instance d'objet, FormatterServices.GetSafeUninitializedObject() lors de la désialisation pour créer une nouvelle instance non initialisée (aucun constructeur n'est appelé) et FormatterServices.PopulateObjectMembers() pour écrivez les champs au nouvel instane. Si vous n'avez que des types de données simples qui sont sérialisables dans les champs, vous pouvez sérialiser et désérialiser le object[] que vous utilisez pour stocker les données de champ.

  • Votre idée actuelle, qui consiste à écrire manuellement une réplique des objets tiers. Cela peut être très douloureux et ne fonctionne essentiellement que si la sérialisation XML fonctionne également. Si les propriétés sont en lecture seule pour sinatce, vous n'irez pas loin avec cette approche.

+0

Merci pour votre réponse.Mais maintenant je suis vraiment à la perte. MyCustomObject contient des méthodes et des informations d'état - comme tout autre objet - que je dois stocker et récupérer à l'autre extrémité. Comment fait-on ça?? Je travaille sur cela depuis des jours maintenant :( –

Questions connexes