Десериализовать типы, перемещаемые между сборками

У меня есть куча типов, которые были перемещены из одной сборки в другую. Я пытаюсь десериализовать данные, которые были сериализованы с помощью старой сборки, в типы в новой сборке с помощью SerializationBinder.

EDIT: Корневое пространство имен сборки совпадает с именем сборки. Старой сборки больше нет.

sealed class TypeMapBinder : SerializationBinder
    {
        public override Type BindToType( string assemblyName, string typeName )
        {
            Type typeToDeserialize = null;

            if ( assemblyName.Contains( "old namespace" ) )
                typeToDeserialize = Type.GetType( typeName.Replace( "old namespace", "new namespace" ) );
            else
                typeToDeserialize = Type.GetType( String.Concat( typeName, ", ", assemblyName ) );

            return typeToDeserialize;
        }
    }

код десериализации выглядит примерно так -

using ( MemoryStream ms = new MemoryStream( byteArr ) )             {
                BinaryFormatter formatter = new BinaryFormatter( );
                formatter.Binder = new TypeMapBinder( );
                return formatter.Deserialize( ms );             
}

Когда я пытаюсь десериализовать, я получаю сообщение об ошибке при попытке загрузить старую сборку.

Не удалось загрузить файл или сборку «старая сборка» или одну из ее зависимостей. Система не может найти указанный файл.


person Vinay B R    schedule 08.01.2013    source источник
comment
Ваше переопределение BindToType вызывается вообще? Составляет ли он правильное имя типа?   -  person Marcel N.    schedule 08.01.2013
comment
да, проблем нет, typeToDeserialize никогда не равен нулю   -  person Vinay B R    schedule 08.01.2013
comment
Поиск соответствия между старым пространством имен и AssemblyName не имеет смысла. Очевидно, что это должно быть старое имя сборки. Вы запутали этот код до такой степени, что его больше нельзя диагностировать, что с ним может быть не так.   -  person Hans Passant    schedule 08.01.2013
comment
@HansPassant: вероятно, он использует схему именования для сборок, где все они имеют свое базовое пространство имен в качестве имени.   -  person Marcel N.    schedule 08.01.2013
comment
@HansPassant: поскольку marceln упоминает, что имя сборки и корневое пространство имен в моем случае совпадают, я должен был упомянуть об этом в вопросе.   -  person Vinay B R    schedule 09.01.2013


Ответы (3)


Я думаю, что вижу ту же проблему.

Метод BindToType моего SerializationBinder не выдает никакого типа, который ссылается на старую сборку, но тем не менее BinaryFormatter пытается загрузить эту старую сборку:

System.IO.FileNotFoundException : Could not load file or assembly 'Old.Interfaces, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
   at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, ref StackCrawlMark stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, ref StackCrawlMark stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, ref StackCrawlMark stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection)
   at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, ref StackCrawlMark stackMark, Boolean forIntrospection)
   at System.Reflection.Assembly.Load(String assemblyString)
   at System.UnitySerializationHolder.GetRealObject(StreamingContext context)
   at System.Runtime.Serialization.ObjectManager.ResolveObjectReference(ObjectHolder holder)
   at System.Runtime.Serialization.ObjectManager.DoFixups()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
   [...]

Если я добавлю обработчик для AppDomain.CurrentDomain.AssemblyResolve для загрузки New.Interfaces вместо Old.Interfaces, он выдаст другое исключение:

System.TypeLoadException : Could not load type 'Old.Interfaces.MyClass' from assembly 'New.Interfaces, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at System.Reflection.RuntimeAssembly.GetType(RuntimeAssembly assembly, String name, Boolean throwOnError, Boolean ignoreCase, ObjectHandleOnStack type)
   at System.Reflection.RuntimeAssembly.GetType(String name, Boolean throwOnError, Boolean ignoreCase)
   at System.UnitySerializationHolder.GetRealObject(StreamingContext context)
   at System.Runtime.Serialization.ObjectManager.ResolveObjectReference(ObjectHolder holder)
   at System.Runtime.Serialization.ObjectManager.DoFixups()

Однако метод BindToType уже был вызван для типа Old.Interfaces.MyClass, и, как я уже сказал, в BindToType я не возвращаю ни одного типа, который может иметь ссылки на старый класс.

Кроме того, если я изменю двоичные данные, которые десериализуются, так что вхождения строки Old будут заменены на New, граф объектов будет окончательно загружен. Я не очень доволен этим решением.

person uebe    schedule 11.04.2013

Я только что сам столкнулся с этой проблемой, и ее практически невозможно исправить.

Я перенес некоторые типы из одной сборки в другую, и теперь файлы, которые я сериализовал в старой версии, не могут быть десериализованы. Мой SerializationBinder успешно разрешает каждый отдельный тип (включая тот, который в конечном итоге вызывает исключение), но я все еще получаю сообщение об ошибке. Он не падает до тех пор, пока позже, в разделе кода, запущенном подпрограммой DoFixup в десериализаторе.

Оказывается, один из моих сериализованных типов имеет член типа Type (знаю, это сбивает с толку). Информация о типе, хранящаяся в этом свойстве Type, при десериализации НЕ проходит через SerializationBinder, а разрешается внутренне и завершается ошибкой. AssemblyResolve тоже не исправит.

Единственный способ десериализовать этот файл, помимо ручного разбора файла, — это обернуть старую версию предыдущими сборками, которые могут ее прочитать, а затем сохранить ее во что-то нейтральное по типу.

person jpwkeeper    schedule 27.03.2019

Я также столкнулся с той же самой проблемой: тип был перемещен в другую сборку, есть настраиваемое связывание, которое возвращает правильный тип, и все же генерируется исключение TypeLoadException.

И на самом деле помогло следующее: https://stackoverflow.com/a/19490593/434298 Я имею в виду добавление [assembly:TypeForwardedTo(typeof(TheType))] к сборке, которая изначально имела этот тип...

поэтому для вашей проблемы я бы предложил попробовать это и, если потребуется, сделать сборку без настоящего исходного кода, а просто [сборка:]

надеюсь, это поможет еще одной бедняге :|

person Runaurufu    schedule 17.06.2020