2010-01-22 83 views
13

我得到一個:的BinaryFormatter反序列化給SerializationException

System.Runtime.Serialization.SerializationException:找不到 集「myNameSpace對象,版本= 1.0.0.0,文化=中立, 公鑰=空

當試圖反序列化另一個程序中的一些數據而不是我序列化它的程序時。

一些谷歌搜索後,我發現顯然這隻能使用共享程序集來完成。

但是,我的數據庫充滿了這個序列化的對象,我需要一個實用程序來讓它們出來。有沒有辦法來覆蓋這種行爲,只是餵它完全相同的類,並強制它反序列化?


我已經找到了這個片段,但我不明白如何以及在哪裏放置/使用它。

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

在實用的應用程序引用原始程序集是不是一種選擇? – 2010-01-22 19:58:05

+0

@eric:有些只是沒有回答...所以我不能接受他們。但我會再次檢查它們,看看是否已添加新答案/評論 – Toad 2010-01-22 20:08:36

+0

任何帶有完整源代碼示例的最終解決方案? – Kiquenet 2013-02-12 09:01:08

回答

6

您需要提供原始類型莫名其妙使該實用程序知道如何反序列化的引用。

簡單的方法就是添加類型最初定義的DLL作爲實用工程的參考。

您發佈的代碼允許您在解串器確定無法找到該類型時動態加載同一個DLL。這是一個比較困難的方法(但不是困難),但是在這兩種情況下,您將需要一個定義類型的DLL ......所以最簡單的方法就是通過添加引用靜態鏈接。

如果您的類型目前不在DLL中(例如,如果它們在EXE中),我建議您將EXE中的類拖入新的DLL中,並從原始項目和util項目。

+0

太棒了!實際上我只是引用了整個.exe文件,以查看它是否有效,並且注意到我可以引用命名空間和類。謝謝你的提示! – Toad 2010-01-22 20:09:22

13

可以解決這個問題,而不需要DLL,如果你知道對象...

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

使用「System.Runtime.Serialization.SerializationBinder」級。由 繼承此類可以重定向所有請求 從二進制格式化程序的類型到您選擇的類型。

這裏是一個樣本,將允許類型在當前彙編發現無論哪個版本大會的最初創建的序列化流:

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

謝謝!我從這開始,但不得不大量修改我的用例的代碼。在這裏看到我的答案:http://stackoverflow.com/a/25412474/1339280 – shoelzer 2014-08-20 19:16:49

+1

爲什麼'ms.Close()'?使用說明沒有注意到這一點嗎? – 2016-06-30 09:55:56

+0

是啊...這絕對是多餘的,猜測從一個非使用語句複製過來,並且從未刪除 – JTtheGeek 2016-06-30 20:07:26

1

如果您沒有訪問原始程序集序列化數據,然後您可以使用SerializationBinder或SerializationSurrogate。這兩個接口允許您在反序列化時控制類型在彼此之間的轉換方式。

4

我遇到了類似的問題,我得到了它與下面的鏈接幫助工作: BinaryFormatterDeserialize-not-finding-a-type

基本上你需要做的是訂閱AssemblyResolve事件之前反序列化。然後反序列化後退訂..

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

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

這裏的方法我用來解決大會:

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

這種技術對我來說無效,因爲我沒有訪問特定版本的反序列化版本,但我知道當前版本是兼容的用它。 – Mike 2012-07-30 17:27:22

0

我接着JTtheGeek,爲了回答瞭解決方案,使它爲我工作,我不得不添加以下語句assemblyName = currentAssembly;

typeName = "yourNamespace.yourClassName"; 

之後,它工作得很好!

4

JTtheGeek's answer是正確的,自定義SerializationBinder是處理此問題的方法。雖然,該答案中給出的示例代碼不足以滿足我的用例。我需要的東西,可以:

  1. 把手匹配的版本號命名空間。
  2. 查看所有組件,而不僅僅是當前的組件。
  3. 仍然足夠快,可以每秒調用100次。

這是我想出了:

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