BinaryFormatter. Выполнить сериализацию "невозможно найти сборку" после ILMerge - PullRequest
12 голосов
/ 02 марта 2011

У меня есть решение C # со ссылочной DLL (также C # с той же версией .Net). Когда я строю решение и запускаю полученный exe, не объединяя exe и dll, на который ссылаются, все работает нормально.

Теперь я хочу объединить их в один exe. Я запускаю ILMerge и все работает нормально. Я пытаюсь выполнить исполняемый файл, и он, кажется, работает нормально, пока он не попытается десериализовать объект, определенный в указанной 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?

Ответы [ 9 ]

36 голосов
/ 26 января 2012

Вы можете сделать это, создав и добавив подкласс 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);
7 голосов
/ 02 марта 2011

Похоже, вы сериализовали объект внутри DLL, затем объединили все сборки с ILMerge и теперь пытаетесь десериализовать этот объект. Это просто не сработает. Процесс десериализации для двоичной сериализации будет пытаться загрузить тип объекта из исходной DLL. Эта библиотека DLL не существует после ILMerge и, следовательно, десериализация завершится неудачно.

Процесс сериализации и десериализации должен работать как до, так и после слияния. Это не может быть смешано

5 голосов
/ 17 января 2017

SerializationBinder также был моим решением. Но у меня есть класс в DLL, на который ссылаются. Поэтому я должен искать во всех загрузочных сборках. Я изменил bevor ответов с параметром, если связыватель должен искать в 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
3 голосов
/ 02 марта 2011

Возможно, вы сериализовали это из отдельной сборки, а затем попытались десериализовать его с другой сборкой (или более новой версией той же сборки).

Некоторое обсуждение здесь

2 голосов
/ 01 августа 2018

Для тех, кто столкнулся с этой проблемой, пытаясь десериализовать из другой сборки, я нашел это решение, которое мне кажется полезным, используя небольшой класс "BindChanger" с общим пространством имен для рассматриваемого типа объекта. https://www.daniweb.com/programming/software-development/threads/339638/deserializing-in-a-different-assembly

0 голосов
/ 01 июня 2014
    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;
    }
}
0 голосов
/ 16 января 2014

я получил решение

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

}

Проблема десериализации: ошибка при десериализации из другой версии программы

0 голосов
/ 30 июля 2013

У меня была ситуация, когда сериализованные данные хранились на сервере SQL старой службой .NET, которая существовала годами. Мне нужно было получить данные из 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 голосов
/ 05 августа 2011

Если вы объединяете сборки в существующую (например, все библиотеки DLL в EXE), вы можете использовать решение, предложенное в этот ответ :

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

Это как минимумработает для десериализации предварительного слияния.IL-Merge также все еще проходит;даже если вы не можете скомпилировать с пересылкой типа к типу той же сборки ...

Я не пробовал, если сериализация работает после слияния.Но я буду держать мой ответ обновленным.

...