2010-01-22 16 views
13

Ich erhalte ein:BinaryFormatter deserialize gibt SerializationException

System.Runtime.Serialization.SerializationException: Kann nicht Assembly ‚MyNamespace, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null

finden

Beim Deserialisieren einiger Daten in einem anderen Programm als das Programm serialisierte ich es mit.

Nach einigem googeln habe ich herausgefunden, dass dies anscheinend nur mit einer gemeinsamen Baugruppe getan werden kann.

Allerdings ist meine Datenbank voll mit diesen serialisierten Objekten, und ich brauche ein Dienstprogramm, um sie herauszubekommen. Gibt es eine Möglichkeit, dieses Verhalten zu überschreiben und es genau die gleiche Klasse zu füttern und es deserialize zwingen?


Ich habe dieses Snippet bereits gefunden, aber ich verstehe nicht, wie und wo ich dieses verwenden/verwenden soll.

static constructor() { 
     AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); 
    } 

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { 
     Assembly ayResult = null; 
     string sShortAssemblyName = args.Name.Split(',')[0]; 
     Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 
     foreach (Assembly ayAssembly in ayAssemblies) { 
      if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) { 
       ayResult = ayAssembly; 
       break; 
      } 
     } 
     return ayResult; 
    } 
+1

die ursprüngliche Montage Referenzierung in Ihrem Dienstprogramm App ist keine Option? –

+0

@eric: einige sind nur noch nicht beantwortet ... also kann ich sie nicht akzeptieren. Aber ich werde sie erneut überprüfen, um zu sehen, ob neue Antworten/Kommentare hinzugefügt wurden – Toad

+0

eine endgültige Lösung mit vollem Quellcode Beispiel? – Kiquenet

Antwort

6

Sie müssen einen Verweis auf die ursprüngliche Art schaffen irgendwie, so dass das Hilfsprogramm weiß, wie es deserialisieren.

Der einfache Weg besteht darin, nur die DLL hinzuzufügen, die ursprünglich als Referenz auf das Dienstprogrammprojekt definiert wurde.

Der von Ihnen gepostete Code ermöglicht es Ihnen, dieselbe DLL dynamisch zu laden, wenn der Deserializer feststellt, dass er den Typ nicht finden kann. Dies ist ein schwieriger Ansatz (aber nicht , dass schwierig), aber in beiden Fällen benötigen Sie eine DLL, die die Typen definiert ... so wahrscheinlich am einfachsten nur statisch zu verknüpfen, indem Sie die Referenz hinzufügen.

Wenn Ihre Typen derzeit nicht in einer DLL sind (z. B. wenn sie in einer EXE sind), empfehle ich, die Klassen aus der EXE in eine neue DLL zu ziehen und diese DLL sowohl vom ursprünglichen Projekt als auch von der util Projekt.

+0

Großartig! Ich habe eigentlich nur auf die gesamte EXE-Datei verwiesen, nur um zu sehen, ob sie funktioniert hat, und ich konnte auf den Namespace und die Klassen verweisen. Danke für den Tipp! – Toad

13

Sie können, um dieses Problem zu bekommen, ohne die DLL zu benötigen, wenn Sie das Objekt kennen ...

http://spazzarama.com/2009/06/25/binary-deserialize-unable-to-find-assembly/

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder(VS.71).aspx

Verwenden Sie die „System.Runtime.Serialization.SerializationBinder“ -Klasse . Durch erben von dieser Klasse ist es möglich, alle Anfragen für Typen aus dem binären Formatierer auf die Typen Ihrer Wahl umleiten.

Hier ist ein Beispiel, das die Typen in der aktuellen Baugruppe unabhängig davon, welche Version der Assembly ursprünglich geschaffen, um die serialisierten Stream gefunden dienlich sein kann,:

sealed class AllowAllAssemblyVersionsDeserializationBinder : System.Runtime.Serialization.SerializationBinder 
{ 
    public override Type BindToType(string assemblyName, string typeName) 
    {  
     String currentAssembly = Assembly.GetExecutingAssembly().FullName; 

     // In this case we are always using the current assembly 
     assemblyName = currentAssembly; 

     // Get the type using the typeName and assemblyName 
     Type typeToDeserialize = Type.GetType(String.Format("{0}, {1}", 
      typeName, assemblyName)); 

     return typeToDeserialize; 
    } 
} 

public static MyRequestObject Deserialize(byte[] b) 
{ 
    MyRequestObject mro = null; 
    var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
    using (var ms = new System.IO.MemoryStream(b)) 
    { 
     // To prevent errors serializing between version number differences (e.g. Version 1 serializes, and Version 2 deserializes) 
     formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder(); 

     // Allow the exceptions to bubble up 
     // System.ArgumentNullException 
     // System.Runtime.Serialization.SerializationException 
     // System.Security.SecurityException 
     mro = (MyRequestObject)formatter.Deserialize(ms); 
     ms.Close(); 
     return mro; 
    } 
} 
+0

Danke! Ich begann damit, musste aber den Code für meinen Anwendungsfall stark modifizieren. Siehe meine Antwort hier: http://stackoverflow.com/a/25412474/1339280 – shoelzer

+1

Warum die 'ms.Close()'? Entspricht das nicht der using-Anweisung? –

+0

yeah ... das ist definitiv überflüssig, raten von einer non using-Anweisung kopiert und nie entfernt – JTtheGeek

1

Wenn Sie keinen Zugang zu die ursprüngliche Assembly, die die Daten serialisiert hat, können Sie einen SerializationBinder oder ein SerializationSurrogate verwenden. Mit diesen beiden Schnittstellen können Sie steuern, wie Typen beim Deserialisieren untereinander konvertiert werden.

4

Ich lief in ein ähnliches Problem, und ich habe es mit Hilfe des folgenden Links arbeiten: BinaryFormatterDeserialize-not-finding-a-type

Im Grunde, was Sie ist auf die AssemblyResolve Ereignis tun müssen, abonnieren VOR Deserialisieren. Dann abmelden nach der Deserialisierung ..

AppDomain.CurrentDomain.AssemblyResolve += 
       new ResolveEventHandler(CurrentDomain_AssemblyResolve); 
// CODE TO DESERIALIZE HERE 

AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve); 

Hier ist die Methode, die ich die Versammlung aufzulösen verwendet:

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
{ 
    try 
    { 
     if(args.Name == "MY ASSEMBLY NAME")) 
     { 
      //Load my Assembly 
      Assembly assem = Assembly.LoadFrom("MY ASSEMBLY PATH"); 
      if(assem != null) 
       return assem; 
     } 
    } 
    catch { ;} 

    return Assembly.GetExecutingAssembly(); 
} 
+0

diese Technik arbeitete für mich, als ich keinen Zugriff auf die bestimmte Version der Assembly für Deserialisierung hatte, aber ich wusste, dass die aktuelle Version kompatibel war damit. – Mike

0

ich die Lösung gefolgt beantwortet von JTtheGeek und um es für mich zu bekommen arbeiten musste ich hinzufügen die folgenden kurz vor der Erklärung assemblyName = currentAssembly;:

typeName = "yourNamespace.yourClassName"; 

Danach es funktionierte großartig!

4

JTtheGeek's answer ist richtig, dass eine benutzerdefinierte SerializationBinder ist die Möglichkeit, dieses Problem zu behandeln. Der in dieser Antwort angegebene Beispielcode ist jedoch für meinen Anwendungsfall nicht ausreichend. Ich brauchte etwas, das konnte:

  1. Handle nicht übereinstimmen Versionsnummern und Namespaces.
  2. Suchen Sie in allen Baugruppen, nicht nur die aktuelle.
  3. Immer noch schnell genug sein, um 100 Mal pro Sekunde anzurufen.

Das ist, was ich kam mit:

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

namespace Company.Product.Common.Serialize 
{ 
    /// <summary> 
    /// A Serialization Binder that allows inexact matches (version number or namespace). 
    /// </summary> 
    public sealed class AllowInexactMatchSerializationBinder : System.Runtime.Serialization.SerializationBinder 
    { 
     static private Dictionary<string, Type> typeBindings = new Dictionary<string, Type>(); 

     /// <summary> 
     /// When overridden in a derived class, controls the binding of a serialized object to a type. 
     /// </summary> 
     /// <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly" /> name of the serialized object.</param> 
     /// <param name="typeName">Specifies the <see cref="T:System.Type" /> name of the serialized object.</param> 
     /// <returns> 
     /// The type of the object the formatter creates a new instance of. 
     /// </returns> 
     public override Type BindToType(string assemblyName, string typeName) 
     { 
      Type type; 
      var assemblyQualifiedTypeName = String.Format("{0}, {1}", typeName, assemblyName); 

      // use cached result if it exists 
      if (typeBindings.TryGetValue(assemblyQualifiedTypeName, out type)) 
      { 
       return type; 
      } 

      // try the fully qualified name 
      try { type = Type.GetType(assemblyQualifiedTypeName); } 
      catch { type = null; } 

      if (type == null) 
      { 
       // allow any assembly version 
       var assemblyNameWithoutVersion = assemblyName.Remove(assemblyName.IndexOf(',')); 
       var assemblyQualifiedTypeNameWithoutVersion = String.Format("{0}, {1}", typeName, assemblyNameWithoutVersion); 
       try { type = Type.GetType(assemblyQualifiedTypeNameWithoutVersion); } 
       catch { type = null; } 
      } 

      if (type == null) 
      { 
       // check all assemblies for type full name 
       try 
       { 
        type = AppDomain.CurrentDomain.GetAssemblies() 
         .SelectMany(a => a.ExportedTypes) 
         .Where(a => a.FullName == typeName) 
         .FirstOrDefault(); 
       } 
       catch { type = null; } 
      } 

      if (type == null) 
      { 
       // check all assemblies for type name 
       var name = typeName.Split('.').Last(); 
       try 
       { 
        type = AppDomain.CurrentDomain.GetAssemblies() 
         .SelectMany(a => a.ExportedTypes) 
         .Where(a => a.Name == name) 
         .FirstOrDefault(); 
       } 
       catch { type = null; } 
      } 

      typeBindings[assemblyQualifiedTypeName] = type; 
      return type; 
     } 
    } 
} 
Verwandte Themen