15

我有一个带有引用 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 选项吗?

4

10 回答 10

46

您可以通过创建和添加一个 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);
于 2012-01-25T23:41:41.533 回答
7

听起来您已经在 DLL 中序列化了一个对象,然后将所有程序集与 ILMerge 合并,现在正试图反序列化该对象。这根本行不通。二进制序列化的反序列化过程将尝试从原始 DLL 加载对象的类型。此 DLL 在 ILMerge 后不存在,因此反序列化将失败。

序列化和反序列化过程都需要在合并前或合并后操作。不能混

于 2011-03-02T16:39:50.893 回答
6

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
于 2017-01-17T08:58:49.723 回答
3

您可能已经从单独的程序集中对其进行了序列化,然后尝试使用另一个程序集(或同一程序集的较新版本)对其进行反序列化。

这里有一些讨论

于 2011-03-02T16:25:24.310 回答
2

对于任何有这个问题的人,试图从不同的程序集反序列化,我发现这个解决方案对我来说似乎很棒,它使用了一个小的“BindChanger”类,它为相关的对象类型使用了一个共享命名空间。 https://www.daniweb.com/programming/software-development/threads/339638/deserializing-in-a-different-assembly

于 2018-08-01T14:05:06.450 回答
2

我为这个问题找到了另一种解决方案。我的问题可能有点不同......

我试图从序列化的同一个库中反序列化,但它找不到正确的程序集来执行此操作。我尝试了上述所有解决方案,但都没有奏效。

我在另一个网站上找到了解决方案(这里

总之,覆盖+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;
}

这为我修复了允许我反序列化的错误。

于 2020-03-12T08:26:55.420 回答
0

如果您将程序集合并到现有程序集(例如所有 DLL 到 EXE),您可以使用此答案中提出的解决方案:

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

这至少适用于反序列化预合并。IL-Merge 也仍然通过;即使您不能使用类型转发编译到相同程序集的类型...

我还没有尝试过,如果序列化在合并后仍然有效。但我会不断更新我的答案。

于 2011-08-05T07:32:19.833 回答
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;
        }
    }
于 2013-07-30T15:02:39.317 回答
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;
}

}

反序列化问题:从不同的程序版本反序列化时出错

于 2014-01-16T09:57:22.640 回答
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;
    }
}
于 2014-06-01T17:08:00.830 回答