我有一个带有引用dll的C#解决方案(也是带有相同.Net版本的C#)。当我构建解决方案并运行生成的exe,而不合并exe和引用的DLL时,一切正常。
现在我想将这些合并到一个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选项吗?
答案 0 :(得分:36)
您可以通过创建和添加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;
}
}
然后在反序列化时将其添加到BinaryFormatter:
BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new PreMergeToMergedDeserializationBinder();
object obj = bf.Deserialize(ms);
答案 1 :(得分:7)
听起来你已经在DLL中序列化了一个对象,然后将所有程序集与ILMerge合并,现在正在尝试反序列化该对象。这根本行不通。二进制序列化的反序列化过程将尝试从原始DLL加载对象的类型。 ILMerge后不存在此DLL,因此反序列化将失败。
序列化和反序列化过程需要在合并前或合并后进行操作。它不能混合
答案 2 :(得分:5)
SerializationBinder也是我的解决方案。但我在引用的DLL中有类。所以我必须搜索所有加载程序集。如果活页夹应在dll中搜索,我已使用参数修改了答案bevor。
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
答案 3 :(得分:3)
您可能已从单独的程序集序列化了该程序集,然后尝试使用其他程序集(或同一程序集的较新版本)对其进行反序列化。
一些讨论here
答案 4 :(得分:2)
对于遇到此问题的任何人,尝试从其他程序集反序列化,我发现此解决方案对我来说非常有用,它使用一个小的“ BindChanger”类和一个有问题的Object类型的共享名称空间。 https://www.daniweb.com/programming/software-development/threads/339638/deserializing-in-a-different-assembly
答案 5 :(得分:2)
我找到了解决该问题的另一种方法。我的问题也许有点不同...
我试图从序列化的同一库中反序列化,但是找不到正确的程序集来进行序列化。我尝试了上面所有的解决方案,但没有一个起作用。
我在另一个网站(Here)上找到了解决方案
简而言之,请在+ tor中重写AppDomain.CurrentDomain.AssemblyResolve的ResolveEventHandler
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
然后实现“ CurrentDomain_AssemblyResolve”的方法,如下所示:
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
System.Reflection.Assembly ayResult = null;
string sShortAssemblyName = args.Name.Split(',')[0];
System.Reflection.Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (System.Reflection.Assembly ayAssembly in ayAssemblies)
{
if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0])
{
ayResult = ayAssembly;
break;
}
}
return ayResult;
}
这为我解决了允许我反序列化的错误。
答案 6 :(得分:0)
如果您将程序集合并到现有程序集(例如,所有DLL到EXE),您可以使用this answer中提出的解决方案:
// AssemblyInfo.cs for My.exe
[assembly: TypeForwardedTo(typeof(SomeTypeFromMergedDLL))]
这至少适用于反合并反序列化。 IL-Merge还会通过;即使您无法使用类型转发编译到同一程序集的类型...
我还没试过,如果序列化工作合并后还没有。但我会更新我的答案。
答案 7 :(得分:0)
我遇到过这样一种情况:序列化数据由一个已经存在多年的旧.NET服务存储在SQL服务器中。我需要从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;
}
}
答案 8 :(得分: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
答案 9 :(得分: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;
}
}