2011-03-02 1 views
9

J'ai une solution C# avec une DLL référencée (aussi C# avec la même version .Net). Quand je construis la solution et exécute l'exe résultant, sans fusionner l'exe et la DLL référencée, tout fonctionne bien.BinaryFormatter.Deserialize "impossible de trouver l'assembly" après ILMerge

Maintenant, je veux les fusionner en un exe. Je lance ILMerge et tout semble fonctionner correctement. J'essaie d'exécuter l'exe et il semble fonctionner correctement jusqu'à ce qu'il essaie de désérialiser un objet défini dans la DLL référencée.

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 
} 

Y at-il peut-être une option ILMerge qui me manque ici?

+3

a fait l'objet sérialisé des ensembles pré-fusion ou d'ensembles post-fusion? –

+1

Je déteste le dire, mais encore une autre raison, je tente toujours de décourager les gens d'utiliser BinaryFormatter. Ce couplage trop proche des métadonnées de type provoque de grandes quantités de douleur. Je peux recommander de meilleures options si vous êtes intéressé à éviter cela (et les nombreux autres scénarios de douleur) à l'avenir. –

+6

Vous savez que la sérialisation binaire n'est pas destinée à être un format de stockage, n'est-ce pas? Il est uniquement destiné à être un format de transport temporaire, il y a trop de limites au stockage à long terme des objets lorsqu'il est effectué via le sérialiseur binaire. Mettez à jour votre programme et remplacez les anciens fichiers. –

Répondre

7

On dirait que vous avez un numéro de série d'un objet dans une DLL, puis fusionner tous les ensembles avec ILMerge et essayez maintenant de désérialiser cet objet. Cela ne fonctionnera tout simplement pas. Le processus de désérialisation de la sérialisation binaire tentera de charger le type de l'objet à partir de la DLL d'origine. Cette DLL n'existe pas après ILMerge et par conséquent la désérialisation échouera.

Le processus de sérialisation et de désérialisation doit fonctionner à la fois avant et après la fusion. Il ne peut être mélangé

3

Vous avez peut-être sérialisé cela à partir de l'assemblage séparé, puis vous avez essayé de le désérialiser avec un autre assemblage (ou une version plus récente du même assemblage).

Une discussion here

0

Dans le cas où la fusion des assemblages dans un existant (par exemple, toutes les DLL à l'EXE) vous pouvez utiliser la solution proposée dans this answer:

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

Ce au moins travaille pour désérialisation avant la fusion. IL-Merge passe aussi toujours; même si vous ne pouvez pas compiler avec un type-forward vers un type du même assembly ...

Je n'ai pas essayé, si la sérialisation fonctionne encore après la fusion. Mais je garderai ma réponse à jour.

33

Vous pouvez le faire en créant et en ajoutant une sous-classe SerializationBinder qui modifiera le nom de l'assembly avant la désérialisation.

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

Puis, quand deserializating ajoutez ceci à BinaryFormatter:

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

Excellente réponse! Cela m'a sauvé beaucoup de temps! Je vous remercie! – gleng

0

J'ai eu une situation où les données sérialisé était stockée dans le serveur SQL par un ancien service .NET qui était en place depuis des années. J'avais besoin de sortir les données de SQL et couru dans ceci aussi. J'ai été capable de me référer au fichier .exe et de le faire fonctionner jusqu'à ce que j'utilise la solution mentionnée ci-dessus. Cependant, les noms de mes assemblées étaient différents.

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

i obtenu la solution

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

Ce serait une bonne idée de donner quelques détails à votre réponse. – julienc

1

SerializationBinder était aussi ma solution. Mais j'ai la classe dans une DLL qui est référencée. Donc je dois chercher dans tous les assemblages de charge.J'ai modifié les réponses bevor avec le paramètre si le classeur devrait chercher dans les DLL.

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

} 

Utilisation:

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 
Questions connexes