2011-03-02 34 views
9

Tengo una solución C# con un dll referenciado (también C# con la misma versión .Net). Cuando construyo la solución y ejecuto el exe resultante, sin combinar el exe y el dll referenciado, todo funciona bien.BinaryFormatter.Deserialize "no se puede encontrar el ensamblado" después de ILMerge

Ahora quiero fusionar estos en un solo exe. Ejecuto ILMerge y todo parece funcionar bien. Intento ejecutar el exe y parece funcionar bien hasta que intente deserializar un objeto definido en el dll al que se hace referencia.

using (Stream fstream = new FileStream(file_path, FileMode.Open)) 
{ 
    BinaryFormatter bf = new BinaryFormatter(); 
    return bf.Deserialize(fstream) as ControlledRuleCollection; 
    // throws unable to find assembly exception 
} 

¿Existe alguna opción de ILMerge que me haga falta aquí?

+3

era el objeto serializado desde los conjuntos de pre-fusión o post-fusionar asambleas? –

+1

Odio decirlo, pero otra razón más es que siempre trato de desalentar a las personas de usar BinaryFormatter. Este acoplamiento demasiado cerrado al tipo de metadatos causa grandes cantidades de dolor. Puedo recomendar mejores opciones si estás interesado en evitar esto (y muchos otros escenarios de dolor) en el futuro. –

+6

¿Sabía que la serialización binaria no pretende ser un formato de almacenamiento, verdad? Solo debe ser un formato de transporte temporal, existen demasiados límites para el almacenamiento a largo plazo de objetos cuando se realiza a través del serializador binario. Actualiza tu programa y adiós a los archivos antiguos. –

Respuesta

7

suena como usted ha serializado un objeto dentro de una DLL, a continuación, se fusionaron todos los conjuntos con ILMerge y ahora está tratando de deserializar ese objeto. Esto simplemente no funcionará. El proceso de deserialización para la serialización binaria intentará cargar el tipo del objeto desde la DLL original. Esta DLL no existe después de ILMerge y, por lo tanto, la deserialización fallará.

El proceso de serialización y deserialización debe funcionar tanto antes como después de la fusión. No se puede mezclar

3

Es posible que haya serializado eso del ensamblaje separado y luego intente deserializarlo con otro ensamblaje (o una versión más reciente del mismo ensamblaje).

Algunos discusión here

0

En caso de combinar asambleas en una ya existente (por ejemplo, todos los archivos DLL a la EXE) puede utilizar la solución propuesta en this answer:

// AssemblyInfo.cs for My.exe 
[assembly: TypeForwardedTo(typeof(SomeTypeFromMergedDLL))] 

Esto funciona para al menos deserializar la precombinación. IL-Merge también todavía pasa; incluso si no puede compilar con un reenvío de tipo a un tipo del mismo ensamblado ...

No lo he intentado, si la serialización funciona aún después de la fusión. Pero mantendré mi respuesta actualizada.

33

Puede hacerlo creando y agregando una subclase de SerializationBinder que cambie el nombre del conjunto antes de que ocurra la deserialización.

sealed class PreMergeToMergedDeserializationBinder : SerializationBinder 
{ 
    public override Type BindToType(string assemblyName, string typeName) 
    { 
     Type typeToDeserialize = null; 

     // For each assemblyName/typeName that you want to deserialize to 
     // a different type, set typeToDeserialize to the desired type. 
     String exeAssembly = Assembly.GetExecutingAssembly().FullName; 


     // The following line of code returns the type. 
     typeToDeserialize = Type.GetType(String.Format("{0}, {1}", 
      typeName, exeAssembly)); 

     return typeToDeserialize; 
    } 
} 

Luego, cuando deserializating añadir esto a la BinaryFormatter:

BinaryFormatter bf = new BinaryFormatter(); 
bf.Binder = new PreMergeToMergedDeserializationBinder(); 
object obj = bf.Deserialize(ms); 
+1

¡Excelente respuesta! ¡Esto me ahorró mucho tiempo! ¡Gracias! – gleng

0

tuve una situación en la que se está almacenando datos serializados en el servidor SQL por un viejo servicio .NET que había estado vigente desde hace años. Necesitaba sacar los datos de SQL y encontré esto también. Pude consultar el archivo .exe y hacerlo funcionar hasta que utilicé la solución mencionada anteriormente. Sin embargo, mis nombres de ensamblado eran diferentes.

sealed class Version1ToVersion2DeserializationBinder : SerializationBinder 
    { 
     public override Type BindToType(string assemblyName, string typeName) 
     { 
      Type typeToDeserialize = null; 

      // For each assemblyName/typeName that you want to deserialize to a different type, set typeToDeserialize to the desired type. 
      String assemVer1 = assemblyName; 
      String typeVer1 = typeName; 

      if (assemblyName == assemVer1 && typeName == typeVer1) 
      { 
       // To use a type from a different assembly version, change the version number. 
       assemblyName = Assembly.GetExecutingAssembly().FullName; 
       // To use a different type from the same assembly, change the type name. 
       typeName = "projectname.typename"; 
      } 

      // The following line of code returns the type. 
      typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName)); 
      return typeToDeserialize; 
     } 
    } 
0

Me dio la solución

sealed class VersionDeserializationBinder : SerializationBinder 
    { 
    public override Type BindToType(string assemblyName, string typeName) 
    { 
    Type typeToDeserialize = null; 
    string currentAssemblyInfo = Assembly.GetExecutingAssembly().FullName; 

    //my modification 
    string currentAssemblyName = currentAssemblyInfo.Split(',')[0]; 
    if (assemblyName.StartsWith(currentAssemblyName))assemblyName = currentAssemblyInfo; 

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

}

Deserialization problem: Error when deserializing from a different program version

0
public sealed class DeserializationBinder : SerializationBinder 
{ 
    private readonly string _typeName; 
    private readonly Assembly _assembly; 
    public DeserializationBinder(Assembly assembly, string typeName) 
    { 
     _typeName = typeName; 
     _assembly = assembly; 
    } 

    public override Type BindToType(string assemblyName, string typeName) 
    { 
     Type typeToDeserialize = null; 
     if (!assemblyName.Contains("System") && !assemblyName.Contains("mscorlib")) 
     { 
      String currentAssembly = _assembly.FullName; 
      assemblyName = currentAssembly; 
      typeName = _typeName; 
     } 
     typeToDeserialize = Type.GetType(String.Format("{0}, {1}", 
      typeName, assemblyName)); 
     return typeToDeserialize; 
    } 
} 
+1

Sería una buena idea dar algunos detalles a su respuesta. – julienc

1

SerializationBinder también fue mi solución. Pero tengo la clase en una DLL a la que se hace referencia. Entonces tengo que buscar en todos los ensamblajes de carga.He modificado las respuestas bevor con el parámetro si la carpeta debe buscar en dlls.

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Reflection; 
using System.Runtime.Serialization; 
using System.Runtime.Serialization.Formatters.Binary; 

namespace ibKastl.Helper 
{ 
    public static class BinaryFormatterHelper 
    { 
     public static T Read<T>(string filename, Assembly currentAssembly) 
     { 
     T retunValue; 
     FileStream fileStream = new FileStream(filename, FileMode.Open); 

     try 
     { 
      BinaryFormatter binaryFormatter = new BinaryFormatter(); 
      binaryFormatter.Binder = new SearchAssembliesBinder(currentAssembly,true);    
      retunValue = (T)binaryFormatter.Deserialize(fileStream); 
     } 
     finally 
     { 
      fileStream.Close(); 
     } 

     return retunValue; 
     } 

     public static void Write<T>(T obj, string filename) 
     { 
     FileStream fileStream = new FileStream(filename, FileMode.Create); 
     BinaryFormatter formatter = new BinaryFormatter(); 
     try 
     { 
      formatter.Serialize(fileStream, obj); 
     } 
     finally 
     { 
      fileStream.Close(); 
     } 
     } 
    } 

    sealed class SearchAssembliesBinder : SerializationBinder 
    { 
     private readonly bool _searchInDlls; 
     private readonly Assembly _currentAssembly; 

     public SearchAssembliesBinder(Assembly currentAssembly, bool searchInDlls) 
     { 
     _currentAssembly = currentAssembly; 
     _searchInDlls = searchInDlls; 
     } 

     public override Type BindToType(string assemblyName, string typeName) 
     { 
     List<AssemblyName> assemblyNames = new List<AssemblyName>(); 
     assemblyNames.Add(_currentAssembly.GetName()); // EXE 

     if (_searchInDlls) 
     { 
      assemblyNames.AddRange(_currentAssembly.GetReferencedAssemblies()); // DLLs 
     } 

     foreach (AssemblyName an in assemblyNames) 
     { 
      var typeToDeserialize = GetTypeToDeserialize(typeName, an); 
      if (typeToDeserialize != null) 
      { 
       return typeToDeserialize; // found 
      } 
     } 

     return null; // not found 
     } 

     private static Type GetTypeToDeserialize(string typeName, AssemblyName an) 
     { 
     string fullTypeName = string.Format("{0}, {1}", typeName, an.FullName); 
     var typeToDeserialize = Type.GetType(fullTypeName); 
     return typeToDeserialize; 
     } 
    } 

} 

Uso:

const string FILENAME = @"MyObject.dat"; 

// Serialize 
BinaryFormatterHelper.Write(myObject1,FILENAME); 

// Deserialize 
MyObject myObject2 = BinaryFormatterHelper.Read<MyObject>(FILENAME, Assembly.GetExecutingAssembly()); // Current Assembly where the dll is referenced 
Cuestiones relacionadas