SerializationBinder со списком <T>

Я пытаюсь сделать работу BinaryFormatter в разных версиях моей сборки. Фактический класс, который я хочу использовать для десериализации, точно такой же в каждой версии сборки, но при десериализации, поскольку объекты сериализованы, включают имя сборки, из которой они пришли, BinaryFormatter жалуется, что не может найти нужную сборку. Поэтому я создал пользовательский SerializationBinder, который сообщает BinaryFormatter всегда десериализовать текущую версию сборки.

Моя схема работает и может десериализовать объекты правильно, но это не работает, если мой объект является списком T, где T был типом, сериализованным из более старой версии моей сборки.

Есть ли способ сделать эту работу со списками и другими родовыми типами, где параметр типа является классом из моей сборки?

//the object i want to deserialize
class MyObject
{
     public string Name{get;set;}
}

//my binder class
class MyBinder : SerializationBinder
{
    static string assemblyToUse = typeof (MyObject).Assembly.FullName;
    public override Type BindToType(string assemblyName, string typeName)
    {
        var isMyAssembly = assemblyName.StartsWith("oldAssemblyName");
        var assemblyNameToUse = isMyAssembly ? assemblyToUse : assemblyName;
        var tn = typeName + ", " + assemblyNameToUse;
        return Type.GetType(tn);            
    }
}


//my deserialize method
static object BinaryDeserialize(string input)
{
    var arr = Convert.FromBase64String(input);
    var ms = new MemoryStream(arr);
    ms.Seek(0, SeekOrigin.Begin);
    var bf = new BinaryFormatter();
    bf.Binder = new MyBinder();
    var obj = bf.Deserialize(ms);

    return obj;
}

static void Test()
{
    //this works
    //serialized(new MyObject());
    var str = ReadSerialized_MyObject();  
    var obj = BinaryDeserialize(str);

    //this doesn't work
    //serialized(new List<MyObject>());
    var str2 = ReadSerialized_List_of_MyObject(); 
    var obj = BinaryDeserialize(str2);
}

Ответ 1

Если вы сериализовали экземпляр List <MyClass> с вашей сборки версии 1.0.0.0, функция SerializationBinder.BindToType будет предложено предоставить этот тип:

System.Collections.Generic.List`1[[MyAssembly.MyClass, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=12345678901234567890]]

Чтобы переназначить список <MyClass> для сборки версии 2.0.0.0, вам нужно изменить имя типа на это:

System.Collections.Generic.List`1[[MyAssembly.MyClass, MyAssembly]]

Главное, чтобы отметить, что имя сборки не полностью соответствует требованиям. Если вы попытаетесь полностью квалифицировать имя сборки с номером версии 2.0.0.0, это не сработает.

Ответ 2

Приложение A создает сериализованный двоичный файл форматирования "SerializedList.bin", который содержит список (результат), где Result - объект Serializable. Теперь приложение B хочет десериализовать файл и загрузить объект List (Result). Вот как я начал работать.

Ссылка: http://social.msdn.microsoft.com/forums/en-US/netfxremoting/thread/eec2b7a6-65f8-42d1-ad4f-409f46bdad61

Приложение A Название сборки - "Сериализация"
Приложение B Название сборки - "DeSerialize"

Код приложения (Сериализация):

namespace Serialize
{
class Program
{
    static void Main(string[] args)
    {            
        List<Result> result = ;//Get From DB

        IFormatter formatter = new BinaryFormatter();
        Stream sStream = new FileStream(
            "SerializedList.bin",
            FileMode.CreateNew,
            FileAccess.Write,
            FileShare.None);

        formatter.Serialize(sStream, result);
        sStream.Close();           
    }
}

}

Некоторые результаты:

[Serializable]
public class Result
{
    public decimal CONTACT_ID { get; set; }
    public decimal INSTITUTION_NBR { get; set; }
}

Код приложения B (DeSerialize):

namespace DeSerialize
{
class Program
{
    static void Main(string[] args)
    {
        IFormatter formatter = new BinaryFormatter();

        string fromTypeName = "System.Collections.Generic.List`1[[Serialize.Result, Serialize, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]";
        string fromTypeName1 = "Serialize.Result";

        string toTypename = "System.Collections.Generic.List`1[DeSerialize.Result]";
        string toTypename1 = "DeSerialize.Result";
        string toTypeAssemblyName = Assembly.GetExecutingAssembly().FullName;

        DictionarySerializationBinder dic = new DictionarySerializationBinder();
        dic.AddBinding(fromTypeName, toTypename);
        dic.AddAssemblyQualifiedTypeBinding(fromTypeName1, toTypename1, toTypeAssemblyName);

        formatter.Binder = dic;

        Stream dStream = new FileStream(
            "SerializeList.bin",
            FileMode.Open,
            FileAccess.Read,
            FileShare.Read);

        List<Result> listDS =
            (List<Result>)formatter.Deserialize(dStream);

        dStream.Close();
    }
}

sealed class DictionarySerializationBinder : SerializationBinder
{
    Dictionary<string, Type> _typeDictionary = new Dictionary<string, Type>();

    public override Type BindToType(string assemblyName, string typeName)
    {
        Type typeToReturn;

        if (_typeDictionary.TryGetValue(typeName, out typeToReturn))
        {
            return typeToReturn;
        }

        else
        {
            return null;
        }
    }

    public void AddBinding(string fromTypeName, string toTypeName)
    {

        Type toType = Type.GetType(toTypeName);

        if (toType == null)
        {
            throw new ArgumentException(string.Format(
            "Help, I could not convert '{0}' to a valid type.", toTypeName));
        }

        _typeDictionary.Add(fromTypeName, toType);

    }

    public void AddAssemblyQualifiedTypeBinding(string fromTypeName, string toTypeName, string toTypeAssemblyName)
    {

        Type typeToSerializeTo = GetAssemblyQualifiedType(toTypeAssemblyName, toTypeName);

        if (typeToSerializeTo == null)
        {

            throw new ArgumentException(string.Format(

            "Help, I could not convert '{0}' to a valid type.", toTypeName));

        }

        _typeDictionary.Add(fromTypeName, typeToSerializeTo);

    }

    private static Type GetAssemblyQualifiedType(string assemblyName, string typeName)
    {

        return Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));

    }
}    

}