2011-03-02 71 views
9

我有一個C#解決方案與引用的DLL(也C#具有相同的.Net版本)。當我構建解決方案並運行生成的exe時,如果不合並exe文件和引用的dll,一切正常。BinaryFormatter.Deserialize「無法找到程序集」在ILMerge

現在我想合併成一個exe文件。我運行ILMerge,一切似乎都正常。我試圖執行該exe文件,它似乎運行得很好,直到它試圖反序列化在引用的dll中定義的對象。

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 
} 

是否有一些ILMerge選項我在這裏失蹤?

+3

從預合併組件或後合併組件序列化的對象? – 2011-03-02 16:22:55

+1

我討厭這麼說,但另一個原因是我總是試圖阻止人們使用BinaryFormatter。這種類型元數據過於緊密的耦合會導致大量的痛苦。如果您有興趣在未來避免這種情況(以及其他許多痛苦情況),我可以推薦更好的選擇。 – 2011-03-02 16:43:16

+6

你知道二進制序列化並不是一種存儲格式,對吧?它只是一種臨時傳輸格式,當通過二進制序列化器完成對象的長期存儲時,存在太多限制。升級你的程序和再見舊文件。 – 2011-03-02 16:43:21

回答

7

這聽起來像你已經連載一個DLL內的對象,然後合併所有ILMerge的組件,並正在試圖反序列化對象。這根本行不通。二進制序列化的反序列化過程將嘗試從原始DLL加載對象的類型。這個DLL在ILMerge後不存在,因此反序列化將失敗。

序列化和反序列化過程需要在合併前或合併後進行操作。它不能混用

3

您可能已將序列化來自單獨的程序集,然後嘗試使用另一個程序集(或相同程序集的較新版本)對其進行反序列化。

一些討論here

0

如果您合併組件到一個現有的(換例如,所有的DLL的EXE),可以使用在this answer提出的解決方案:

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

這至少適用於反序列化預合併。 IL-Merge也依然通過;即使你不能編譯類型轉發到一個類型相同的程序集...

我還沒有嘗試過,如果序列化工作後合併還沒有。但我會保持我的答案更新。

33

您可以通過創建並添加SerializationBinder子類來完成此操作,該子類將在反序列化發生之前更改程序集名稱。

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

然後deserializating時將其添加到的BinaryFormatter:

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

優秀的答案!這爲我節省了很多時間!謝謝! – gleng 2015-04-29 19:51:25

0

我有哪裏序列化的數據正在由已經存在多年的老.NET服務存儲在SQL Server的情況。我需要從SQL中獲取數據,並且也會遇到這種情況。我能夠引用.exe並使其工作,直到我使用上述解決方案。但是我的程序集名稱不同。

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

我得到了解決

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

給你的答案提供一些細節是個好主意。 – julienc 2014-06-01 17:25:44

1

SerializationBinder也是我的解決方案。但我有一個引用的DLL中的類。所以我必須搜索所有負載組件。如果活頁夾應該在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; 
     } 
    } 

} 

用法:

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 
相關問題