I have an assembly that saves and loads (or tries to) an IEnumerable<> to/from a persisted binary stream.
If the assembly is loaded, and a serialisation of the data is performed before a deserialisation, everything works fine and the data is deserialised and loaded as expected, with no exceptions thrown.
However, if the assembly is loaded and a request is made to deserialise the (persisted) data before serialisation occurs, the following error is generated:
The MyClass could not be transferred.
Unable to load type System.Collections.Generic.List`1[[MyClass, MyAssembly,
Version=1.2.0.0, Culture=neutral, PublicKeyToken=null]] required for deserialization.
i have read many articles on serialising ienumerables and i am somewhat confused by what i have read, as many sources state it should not be possible, but this code has apparently worked in the past (i have since moved some types between assemblies (which appears to be the cause of the issue) but i cant move them back).
why does it only work if the serialisation takes places first? why cant it find the type if serialisation doesnt occur? why do i not get this error on serialisation?
public void Load<T>(T instance)
{
Transfer(instance, (binaryFormatter, property, key, inst) =>
{
object value = PropertyBag.Read(key);
if(null != value)
{
using(MemoryStream memoryStream = new MemoryStream(value as byte[]))
{
property.SetValue(inst, binaryFormatter.Deserialize(memoryStream), null);
}
}
});
}
public void Save<T>(T instance)
{
Transfer(instance, (binaryFormatter, property, key, inst) =>
{
object value = property.GetValue(inst, null);
using(MemoryStream memoryStream = new MemoryStream())
{
binaryFormatter.Serialize(memoryStream, value);
PropertyBag.Write(key, memoryStream.GetBuffer());
}
});
}
internal sealed class AssemblyBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
Type typeDefinition = null;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
try
{
string isolatedAssemblyName = assemblyName.Split(',')[0];
foreach(Assembly assembly in assemblies)
{
if(assembly.FullName.Split(',')[0] == isolatedAssemblyName)
{
typeDefinition = assembly.GetType(typeName);
break;
}
}
}
catch(System.Exception ex)
{
LogManager.Logger.Log(new LoggableException(ex));
}
return typeDefinition;
}
}
private void Transfer<T>(T instance, Action<BinaryFormatter, PropertyInfo, string, T> action)
{
Type type = typeof(T);
PropertyInfo[] properties = type.GetProperties(
GetBindingFlags());
foreach(PropertyInfo property in properties)
{
try
{
BaggableAttribute[] attributes =
property.GetCustomAttributes(typeof(BaggableAttribute), true)
as BaggableAttribute[];
if(attributes == null || attributes.Length == 0)
continue;
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Binder = new AssemblyBinder();
action.Invoke(binaryFormatter, property, attributes[0].Key, instance);
}
catch(Exception ex)
{
Logger.Log(new PropertyTransferException(ex, property.Name));
}
}
}
private BindingFlags GetBindingFlags()
{
return BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance;
}
Thanks for your time!