2011-04-26 19 views
9

Estoy tratando de hacer que el BinaryFormatter funcione en diferentes versiones de mi ensamblaje. La clase real a la que quiero deserializar es exactamente la misma en cada versión de ensamblaje, pero en la deserialización, debido a que los objetos están serializados incluyen el nombre del ensamblaje del que provienen, el BinaryFormatter se queja de que no puede encontrar el ensamblaje correcto. Así que creé un SerializationBinder personalizado que le dice al BinaryFormatter que siempre se deserialice a la versión de ensamblaje actual.SerializationBinder con la lista <T>

Mi esquema funciona y puede deserializar objetos correctamente, pero no funciona si mi objeto es una Lista de T, donde T era un tipo serializado de una versión anterior de mi ensamblaje.

¿Hay alguna manera de hacer que esto funcione con listas y otros tipos genéricos donde el parámetro tipo es una clase de mi ensamblado?

//the object i want to deserialize 
class MyObject 
{ 
    public string Name{get;set;} 
} 

//my binder class 
class MyBinder : SerializationBinder 
{ 
    static string assemblyToUse = typeof (MyObject).Assembly.FullName; 
    public override Type BindToType(string assemblyName, string typeName) 
    { 
     var isMyAssembly = assemblyName.StartsWith("oldAssemblyName"); 
     var assemblyNameToUse = isMyAssembly ? assemblyToUse : assemblyName; 
     var tn = typeName + ", " + assemblyNameToUse; 
     return Type.GetType(tn);    
    } 
} 


//my deserialize method 
static object BinaryDeserialize(string input) 
{ 
    var arr = Convert.FromBase64String(input); 
    var ms = new MemoryStream(arr); 
    ms.Seek(0, SeekOrigin.Begin); 
    var bf = new BinaryFormatter(); 
    bf.Binder = new MyBinder(); 
    var obj = bf.Deserialize(ms); 

    return obj; 
} 

static void Test() 
{ 
    //this works 
    //serialized(new MyObject()); 
    var str = ReadSerialized_MyObject(); 
    var obj = BinaryDeserialize(str); 

    //this doesn't work 
    //serialized(new List<MyObject>()); 
    var str2 = ReadSerialized_List_of_MyObject(); 
    var obj = BinaryDeserialize(str2); 
} 

Respuesta

7

Si serializado una instancia de lista < MiClase> desde su ensamblaje versión 1.0.0.0, se le pedirá a la función SerializationBinder.BindToType para proporcionar este tipo:

System.Collections.Generic.List`1[[MyAssembly.MyClass, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=123456789]] 

Con el fin de volver a asignar el Lista < MiClase> tipo de montaje a su versión 2.0.0.0, es necesario cambiar el nombre del tipo a esto:

System.Collections.Generic.List`1[[MyAssembly.MyClass, MyAssembly]] 

el punto principal observar, es que el nombre del ensamblado no está completamente calificado. Si intenta calificar completamente el nombre del ensamblado con un número de versión 2.0.0.0, no funcionará.

+0

esto es lo que terminé haciendo ... apunta a que ya me olvidé de responder a esta misma – dan

+0

Esto sólo podría salvar mi vida Enviar esta lista de más de 1000 es de aproximadamente 3 MB de tamaño de mensaje una vez SOAPified utilizando un punto final de un servicio básico WCttBinding. Ahora que estoy codificando binariamente el mensaje a mano, tengo alrededor de .4MB. Eres un salvavidas! – Aejay

1

La aplicación A crea un archivo formateador binario serializado "SerializedList.bin" que contiene la lista (resultado) donde Result es el objeto serializable. Ahora la aplicación B quiere desSerializar el archivo y cargarlo en el objeto Lista (Resultado). Así es como yo tengo trabajo ..

Referencia: http://social.msdn.microsoft.com/forums/en-US/netfxremoting/thread/eec2b7a6-65f8-42d1-ad4f-409f46bdad61

Aplicación A Nombre Asamblea es "Serializar"
Nombre de la aplicación de la Asamblea B es "Deserialize"

Aplicación Código A (Serialize):

namespace Serialize 
{ 
class Program 
{ 
    static void Main(string[] args) 
    {    
     List<Result> result = ;//Get From DB 

     IFormatter formatter = new BinaryFormatter(); 
     Stream sStream = new FileStream(
      "SerializedList.bin", 
      FileMode.CreateNew, 
      FileAccess.Write, 
      FileShare.None); 

     formatter.Serialize(sStream, result); 
     sStream.Close();   
    } 
} 

}

Algunos Resultado O bject:

[Serializable] 
public class Result 
{ 
    public decimal CONTACT_ID { get; set; } 
    public decimal INSTITUTION_NBR { get; set; } 
} 

Código B Aplicación (Deserialize):

namespace DeSerialize 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     IFormatter formatter = new BinaryFormatter(); 

     string fromTypeName = "System.Collections.Generic.List`1[[Serialize.Result, Serialize, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"; 
     string fromTypeName1 = "Serialize.Result"; 

     string toTypename = "System.Collections.Generic.List`1[DeSerialize.Result]"; 
     string toTypename1 = "DeSerialize.Result"; 
     string toTypeAssemblyName = Assembly.GetExecutingAssembly().FullName; 

     DictionarySerializationBinder dic = new DictionarySerializationBinder(); 
     dic.AddBinding(fromTypeName, toTypename); 
     dic.AddAssemblyQualifiedTypeBinding(fromTypeName1, toTypename1, toTypeAssemblyName); 

     formatter.Binder = dic; 

     Stream dStream = new FileStream(
      "SerializeList.bin", 
      FileMode.Open, 
      FileAccess.Read, 
      FileShare.Read); 

     List<Result> listDS = 
      (List<Result>)formatter.Deserialize(dStream); 

     dStream.Close(); 
    } 
} 

sealed class DictionarySerializationBinder : SerializationBinder 
{ 
    Dictionary<string, Type> _typeDictionary = new Dictionary<string, Type>(); 

    public override Type BindToType(string assemblyName, string typeName) 
    { 
     Type typeToReturn; 

     if (_typeDictionary.TryGetValue(typeName, out typeToReturn)) 
     { 
      return typeToReturn; 
     } 

     else 
     { 
      return null; 
     } 
    } 

    public void AddBinding(string fromTypeName, string toTypeName) 
    { 

     Type toType = Type.GetType(toTypeName); 

     if (toType == null) 
     { 
      throw new ArgumentException(string.Format(
      "Help, I could not convert '{0}' to a valid type.", toTypeName)); 
     } 

     _typeDictionary.Add(fromTypeName, toType); 

    } 

    public void AddAssemblyQualifiedTypeBinding(string fromTypeName, string toTypeName, string toTypeAssemblyName) 
    { 

     Type typeToSerializeTo = GetAssemblyQualifiedType(toTypeAssemblyName, toTypeName); 

     if (typeToSerializeTo == null) 
     { 

      throw new ArgumentException(string.Format(

      "Help, I could not convert '{0}' to a valid type.", toTypeName)); 

     } 

     _typeDictionary.Add(fromTypeName, typeToSerializeTo); 

    } 

    private static Type GetAssemblyQualifiedType(string assemblyName, string typeName) 
    { 

     return Type.GetType(String.Format("{0}, {1}", typeName, assemblyName)); 

    } 
}  

}

Cuestiones relacionadas