Saturday, August 1, 2015

Failed to deserialize the object due to DLL version/name has changed

When you tried to deserialize the binary to an object but you encountered the following exception:

  BinaryFormatter.Deserialize “unable to find assembly”

Basically, it tells you that it cannot find the DLL by version + name. This is commonly issue when you change the DLL version number or move the class to another project/assembly. As a result, we need a way to tell the BinaryFormatter class what is the correct new "type" for the binary.

In the deserialization process, you need to add a line to use your custom binder:

using (MemoryStream memory = new MemoryStream(user_input))
{
    BinaryFormatter binary = new BinaryFormatter();

    //fix the deserialization error when the DLL version has been changed.
    binary.Binder = new PreMergeToMergedDeserializationBinder();

    // convert the binary to the list.
    this._data = binary.Deserialize(memory) as List<CUserDataItem>;
}

And then add the following class. I have enhanced this class and it is able to handle the generic list as well.

public sealed class PreMergeToMergedDeserializationBinder : System.Runtime.Serialization.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.

        // extract the 'old dll name/version'.
        string old_dll = typeName.ExtractString(',', ']');

        if (old_dll.IsNotEmpty())
        {
            // for generic list, we replace the dll name/version here.
            typeToDeserialize = Type.GetType(typeName.Replace(old_dll, exeAssembly.ToString()));
        }
        else
        {
            // for 1 single object, the 'typeName' is the class name.
            // We should return the type name with the new dll name/version.
            typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
                                                typeName, exeAssembly));
        }

        System.Diagnostics.Debug.Assert(typeToDeserialize != null);

        return typeToDeserialize;
    }
}

I have an string class extension which helps to extract partial string:

public static string ExtractString(this string s,
    char start_char,
    char end_char)
{
    int i = s.IndexOf(start_char);
    if (i >= 0)
    {
        //16.Nov.2011-lhw-the 'end_char' should be search after the 'start_char'.
        int i2 = s.IndexOf(end_char,
                           i + 1);      //16.Nov.2011-lhw-missing the start pos!!

        string tmp = s.Substring(i + 1,
                                 i2 - i - 1);

        return tmp;
    }
    else
    {
        return string.Empty;
    }
}

Reference:
http://stackoverflow.com/questions/5170333/binaryformatter-deserialize-unable-to-find-assembly-after-ilmerge