2012-02-02 13 views
6

Me doy cuenta de que ha sido bien establecido en otros lugares que la serialización de objetos de dominio NHibernate es generalmente una mala idea. Mi pregunta aquí es para tratar de entender cómo funciona BinaryFormatter, y por qué el escenario siguiente produce el InvalidCastException.¿Por qué BinaryFormatter intentará convertir un objeto del tipo marcado [Serializable] en IConvertible?

La estructura de clases más o menos se parece a esto:

[Serializable] 
public class Parent 
{ 
    public virtual Child child{get; set;} 
} 

[Serializable] 
public class Child 
{ 
    public virtual ICollection<GrandChild> GrandChildren { get; set; } 
} 

[Serializable] 
public class GrandChild 
{ 
    public virtual Pet pet{get; set;} 
} 

[Serializable] 
public class Pet 
{ 
    public virtual IList<Toy> Toys { get; set; } 
} 

[Serializable] 
public class Toy 
{ 
    public string ToyName { get; set; } 
} 

El método de serialización es el siguiente:

public static byte[] Serialize(this object t) 
{ 
    using (var ms = new MemoryStream()) 
    { 
     BinarySerializer.Serialize(ms, t); 
     return ms.ToArray(); 
    } 
} 

A veces cuando se llama a la serialización por ejemplo,

Parent p = new Parent() ....; 
p.Serialize(); 

voy a tener

No se puede convertir objeto de tipo 'NHibernate.Collection.Generic.PersistentGenericBag`1 [Juguete]' al tipo 'System.IConvertible'.

(todas las colecciones están mapeadas con la semántica del bolso).

Incluso NHibernate.Collection.Generic.PersistentGenericBag<T> está marcado [Serializable]

Por lo tanto, dado que todo lo que aquí se marca como [Serializable] ¿por qué BinaryFormatter estar intentando echar a un PersistentGenericBag IConvertible en el primer lugar?

Editar: En caso de que sea relevante, esto está bajo .NET 3.5 y 3.1.0 NHibernate

+0

Usted dice que esto ocurre "a veces" al serializar - es el código utilizado en una aplicación multi-hilo? – LukeH

+0

Se usa en una aplicación web, pero en el escenario que estoy viendo, creo que probablemente no sea un problema de concurrencia. Parece que es más probable que esté relacionado con la carga diferida. – Nathan

Respuesta

1

Al tener la clase herede de mascotas System.Runtime.Serialization.ISerializable, ahora tenemos un control completo sobre cómo la clase de mascotas, y su los miembros, en este caso Toy, se serializan y se deserializan. Consulte System.Runtime.Serialization.ISerializable para obtener más información sobre la implementación de System.Runtime.Serialization.ISerializable.

El ejemplo siguiente serializará y luego deserializará una instancia de la clase Parent en una matriz de bytes y viceversa.

El método público, pública GetObjectData (información System.Runtime.Serialization.SerializationInfo, el contexto System.Runtime.Serialization.StreamingContext), se llama cuando el tipo de animal doméstico se publica por entregas; primero agregamos un valor Int32 que indica la cantidad de elementos en la lista de Juguetes. Luego agregamos cada juguete de la lista.

El constructor protegido, protegida (información System.Runtime.Serialization.SerializationInfo, el contexto System.Runtime.Serialization.StreamingContext) Acepta, se llama cuando se publica por entregas de-este tipo. Primero leemos la cantidad de elementos que se almacenaron en la lista de Juguetes, y luego los usamos para leer cada instancia de Toy de la secuencia serializada.

Tenga en cuenta que para cada instancia de Toy agregada a la secuencia serializada, le damos un nombre diferente; en este caso, simplemente añadimos el valor del índice a la palabra Juguete; es decir, "Toy1", "Toy2", ... Esto se debe a que cada elemento de la secuencia serializada necesita un nombre único. Ver: System.Runtime.Serialization.ISerializable.

Y controlando la serialización/de-serialización de la lista de juguetes en Pet, podemos eliminar el problema de no ser capaz de serializar/deserializar la lista en función del tipo: NHibernate.Collection.Generic.PersistentGenericBag.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

static class Program 
{ 
    static void Main(string[] args) 
    { 
     Parent p = new Parent(); 
     p.child = new Child(); 
     p.child.GrandChildren = new List<GrandChild>(); 
     p.child.GrandChildren.Add(new GrandChild { pet = new Pet() }); 
     p.child.GrandChildren.First().pet.Toys = new List<Toy>(); 
     p.child.GrandChildren.First().pet.Toys.Add(new Toy { ToyName = "Test" }); 
     byte[] result = Serialize(p); 
     Parent backAgain = Deserialize(result); 
    } 
    public static System.Runtime.Serialization.Formatters.Binary.BinaryFormatter BinarySerializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
    public static byte[] Serialize(Parent p) 
    { 
     using (var ms = new System.IO.MemoryStream()) 
     { 
      BinarySerializer.Serialize(ms, p); 
      return ms.ToArray(); 
     } 
    } 
    public static Parent Deserialize(byte[] data) 
    { 
     using (var ms = new System.IO.MemoryStream(data)) 
     { 
      return (Parent)BinarySerializer.Deserialize(ms); 
     } 
    } 
} 

[Serializable] 
public class Parent 
{ 
    public virtual Child child { get; set; } 
} 

[Serializable] 
public class Child 
{ 
    public virtual ICollection<GrandChild> GrandChildren { get; set; } 
} 

[Serializable] 
public class GrandChild 
{ 
    public virtual Pet pet { get; set; } 
} 

[Serializable] 
public class Pet : System.Runtime.Serialization.ISerializable 
{ 
    public Pet() { } 

    // called when de-serializing (binary) 
    protected Pet(System.Runtime.Serialization.SerializationInfo info, 
        System.Runtime.Serialization.StreamingContext context) 
    { 
     Toys = new List<Toy>(); 
     int counter = info.GetInt32("ListCount"); 
     for (int index = 0; index < counter; index++) 
     { 
      Toys.Add((Toy)info.GetValue(string.Format("Toy{0}",index.ToString()),typeof(Toy))); 
     } 
    } 

    // called when serializing (binary) 
    public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, 
           System.Runtime.Serialization.StreamingContext context) 
    { 
     info.AddValue("ListCount", Toys.Count); 
     for (int index = 0; index < Toys.Count; index++) 
     { 
      info.AddValue(string.Format("Toy{0}", index.ToString()), Toys[index], typeof(Toy)); 
     } 
    } 

    public virtual IList<Toy> Toys { get; set; } 
} 

[Serializable] 
public class Toy 
{ 
    public string ToyName { get; set; } 
} 
+0

Esto posiblemente puede proporcionar un mecanismo para evitar el problema, pero no explica por qué el extraño molde se estaba produciendo en primer lugar, que es realmente el punto principal de la pregunta. – Nathan

Cuestiones relacionadas