Search code examples
c#.netbinaryformatterbinary-serialization

Deserialize types moved across assemblies


I have a bunch of types which have been moved from one assembly to another. I am trying to deserialize data which was serialized using the old assembly into types in the new assembly by using a SerializationBinder.

EDIT: Root namespace of the assembly is same as the assembly name. The old assembly does not exist anymore.

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

deserialization code looks something like this -

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

When i try to deserialize i get an error where its trying to load the old assembly.

Could not load file or assembly 'old assembly' or one of its dependencies. The system cannot find the file specified.


Solution

  • I think I'm seeing the same problem.

    The BindToType method of my SerializationBinder does not emit any type, which refers to the old assembly, but still, the BinaryFormatter tries to load that old assembly:

    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)
       [...]
    

    If I add a handler for AppDomain.CurrentDomain.AssemblyResolve to load New.Interfaces instead of Old.Interfaces it throws another exception:

    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()
    

    However, the BindToType method has already been called for the type Old.Interfaces.MyClass and as I said, in BindToType I'm not returning a single type which might have references to the old class.

    Furthermore, if i change the binary data which gets deserialized, so that the occurrences of the string Old get replaced by New, the object graph gets finally loaded. I'm not very happy with this solution.