The context is as follows
- I want to refactor code by moving it around to different projects
- Some of this code comprises of serializable DTOs that are used to send and receive data across multiple endpoints
- If I move the code around, serialization breaks (therefore it is not backward compatible with older versions of my application)
A solution to this problem is the SerializationBinder which allows me to "redirect" in a sense from one type to another.
I therefore want to create a SerializationBinder to meet this need. However it must do so by meeting the following requirements
- The inputs to the SerializationBinder should be a list of old type to new type mappings. The mapping should include the old assembly name (no version, no public key token) and the old full name of the type (namespace and name) as well as the new assembly name and new full name of the type
- For the types that are in the inputs, version numbers of assemblies should be ignored
- It should handle generics if my types happen to be in generics (List, Dictionary, etc) without needing to include the generics in the inputs
- For anything that is not in the inputs (i.e. types that have not moved or .NET types like dataset for example) it should default to using the out of the box algorithm of the binary serializer
Is this possible or am I dreaming? Is there something out there that already does this? I would assume this is a common problem.
So far I see no easy way of doing 3 and no way at all of doing 4.
Here is an attempt
public class SmartDeserializationBinder : SerializationBinder
{
/// <summary>
/// Private class to handle storing type mappings
/// </summary>
private class TypeMapping
{
public string OldAssemblyName { get; set; }
public string OldTypeName { get; set; }
public string NewAssemblyName { get; set; }
public string NewTypeName { get; set; }
}
List<TypeMapping> typeMappings;
public SmartDeserializationBinder()
{
typeMappings = new List<TypeMapping>();
}
public void AddTypeMapping(string oldAssemblyName, string oldTypeName, string newAssemblyName, string newTypeName)
{
typeMappings.Add(new TypeMapping()
{
OldAssemblyName = oldAssemblyName,
OldTypeName = oldTypeName,
NewAssemblyName = newAssemblyName,
NewTypeName = newTypeName
});
}
public override Type BindToType(string assemblyName, string typeName)
{
//Need to handle the fact that assemblyName will come in with version while input type mapping may not
//Need to handle the fact that generics come in as mscorlib assembly as opposed to the assembly where the type is defined.
//Need to handle the fact that some types won't even be defined by mapping. In this case we should revert to normal Binding... how do you do that?
string alternateAssembly = null;
string alternateTypeName = null;
bool needToMap = false;
foreach (TypeMapping mapping in typeMappings)
{
if (typeName.Contains(mapping.OldTypeName))
{
alternateAssembly = mapping.NewAssemblyName;
alternateTypeName = mapping.NewTypeName;
needToMap = true;
break;
}
}
if (needToMap)
{
bool isList = false;
if (typeName.Contains("List`1"))
isList = true;
// other generics need to go here
if (isList)
return Type.GetType(String.Format("System.Collections.Generic.List`1[[{0}, {1}]]", alternateTypeName, alternateAssembly));
else
return Type.GetType(String.Format("{0}, {1}", alternateTypeName, alternateAssembly));
}
else
return null; // this seems to do the trick for binary serialization, but i'm not sure if it is supposed to work
}
}