Oct 26 2010

Object Independant Serialization C#.NET

Creating and storing serialized objects in a database is a fairly easy task.  However, the problem comes when you have created serialized objects with one application (or version of an application) and want to deserialize and use them with another.  Usually the error that you get is:

Unable to find assembly 'MyAssembly; Version=1.0.3463.18923;
Culture=neutral; PublicKeyToken=40e3171cc8066fe6'.

What?

Well the issue here is the objects that have been Serialized have a formatter transmits the information required to create an instance of an object of the correct type and version. This information generally includes the full type name and assembly name of the object. The assembly name includes the name, version, and strong name hash of the assembly.  What this ultimately means is that your serialized object data is now tied to the assembly and version that created it.

By default the deserialization process uses this formatter information to recreate the object as the correct type.  What we can do is override what the serialized object says and provide our own assembly information so that we can open our objects in another application.

First what we need is to create some add-ons for the SerializationBinder class that is part of System.Runtime.Serialization

sealed class SerializationHelper :
System.Runtime.Serialization.SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
}
}

What the SerializationBinder class does is allow us to setup the assembly information and find the current object type that we want to deserialize as in this application, as opposed to the one it was created it.

First thing, we create an object of Type to hold the object type that we want to deserialize as

Type typeToDeserialize = null;

The biggest problem I ran into is the way that System.Reflection.Assembly returns assembly information. We can get the full name of the assembly like so

string currentAssembly = Assembly.GetExecutingAssembly().FullName;

Which gives us the fully qualified name of the current assembly, version and culture information. But we don’t need all that.

// In this case we are always using the current assembly
assemblyName = currentAssembly;

int index = typeName.IndexOf('.');
string obj = typeName.Substring(index+1);

index = currentAssembly.IndexOf(',');
currentAssembly = currentAssembly.Substring(0, index);

string objType = currentAssembly + "." + obj;

So here we use a little string magic to extract just the assembly name and just the object name so that we can concatenate them together into a fully qualified object name that GetType() will understand.

// Get the type using the typeName and assemblyName
typeToDeserialize = Type.GetType(objType);

This will return the CLR loaded manifest information on the object that matches the AssemblyName.Objectname that we provided to GetType. We can then return this information.

return typeToDeserialize;

Ultimately, to make sure that your deserialization process will use this new binding formatter you need to create your binary format binder as a new instance of our SerializationHelper class.

BinaryFormatter myFormat = new BinaryFormatter();

//set up a new binder so that we can maintain assembly freedom between
//serialized objects
myFormat.Binder = new SerializationHelper();

This way, when the myFormat.Deserialize() is called, the SerializationHelper class and our new overriden method will fire, causing the assembly information to be changed to the current running assembly, allowing you to deserialize objects that your current application (or version of the same application) did not create.