使用iserializable接口时C#中的版本控制序列化

versioning serialization in c# when using iserializable interface
2021-03-07
  •  译文(汉语)
  •  原文(英语)

我有一个基类FDObjectBase,它继承自ContentControl,ISerializable,INotifyPropertyChanged.现在,使用以下代码逻辑对此类进行反序列化

public FDObjectBase(SerializationInfo info, StreamingContext context) : this()
{
   Left = (double)info.GetValue("Left", typeof(double));
    Top = (double)info.GetValue("Top", typeof(double));
    Height = (double)info.GetValue("Height", typeof(double));
    Width = (double)info.GetValue("Width", typeof(double));
    DesignObjectID = (int)info.GetValue("DesignObjectID", typeof(int));
    ShapeType = (int)info.GetValue("ShapeType", typeof(int));
    Angle = (double)info.GetValue("Angle", typeof(double));
    try
    {
        ObjectType = (ObjectType)info.GetValue("ObjectType", typeof(ObjectType));
        //this.ToolTip = ObjectType.ToString();
    }
    catch { }

    OnDeserialized(new EventArgs());
    //DataObject = (DesignData)info.GetValue("DataObject", typeof(DesignData));
    //this.ToolTip = DataObject.Name + " (" + DataObject.ObjectType.ToString().ToLower() + ")";    

}

这在应用程序中工作正常,但是问题是我希望反序列化过程向后兼容,因为根据对此类的要求更改,一定不能破坏存储在数据库中的反序列化数据.我为此找到的解决方案是将[OptionalField]属性添加到添加到此类的所有新字段中,但是这对我来说还没有解决.当我在此类中添加新字段时,我仍然遇到相同的异常(添加新字段后,不会发生反序列化,并且会引发以下异常)

System.Reflection.TargetInvocationException was caught
  Message=Exception has been thrown by the target of an invocation.
  Source=mscorlib
  StackTrace:
       at System.RuntimeMethodHandle._SerializationInvoke(IRuntimeMethodInfo method, Object target, SignatureStruct& declaringTypeSig, SerializationInfo info, StreamingContext context)
       at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)
       at System.Runtime.Serialization.ObjectManager.FixupSpecialObject(ObjectHolder holder)
       at System.Runtime.Serialization.ObjectManager.DoFixups()
       at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
       at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
       at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
       at .SeatManagement.Client.Helpers.Generics.Deserialize[T](String data) in Generics.cs:line 117
  InnerException: System.Runtime.Serialization.SerializationException
       Message=Member 'Test' was not found.
       Source=mscorlib

因此,我在使用iserializable接口时正在寻找有关版本化序列化C#方向的任何建议,这样即使在向类中添加新字段时它也不会中断.在此先感谢您的帮助.

解决过程1

最终,我找到了解决该问题的方法..但是它没有使用System.Runtime.Serialization提供的即用型功能.我在基类上添加了属性版本

private static int VERSION_NUMBER = 1;

与其他设计属性一起进行序列化,如下所示.

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("Version", VERSION_NUMBER);
    //First Version properties
    info.AddValue("Left", Left);
    info.AddValue("Top", Top);
    info.AddValue("Height", Height);
    info.AddValue("Width", Width);
    info.AddValue("DesignObjectID", DesignObjectID);
    info.AddValue("ShapeType", ShapeType);
    info.AddValue("ObjectType", ObjectType.GetIntValue());
    info.AddValue("Angle", Angle);
    //Second Version Properties
}

现在当我反序列化字符串时,我检查存储在反序列化字符串中的版本

public FDObjectBase(SerializationInfo info, StreamingContext context) : this()
{
    int version = Generics.GetSerializedValue<int>(info, "Version");

    if (version >= 1)
    {
        Left = (double)info.GetValue("Left", typeof(double));
        Top = (double)info.GetValue("Top", typeof(double));
        Height = (double)info.GetValue("Height", typeof(double));
        Width = (double)info.GetValue("Width", typeof(double));
        DesignObjectID = (int)info.GetValue("DesignObjectID", typeof(int));
        ShapeType = (int)info.GetValue("ShapeType", typeof(int));
        Angle = (double)info.GetValue("Angle", typeof(double));
        ObjectType = ((int)info.GetValue("ObjectType", typeof(int))).GetEnumValue<ObjectType>();
    }
    if (version >= 2)
    {
        //Add newly added properties for this version
    }
    else { 
        //Add default values for new properties for this version
    }

    OnDeserialized(new EventArgs());

} 

因此,现在当我添加之前未反序列化的新属性时,我只需增加版本并在第二个if语句下添加新属性即可.这样,存储在数据库中的信息始终向后兼容.它可能不是最好的解决方案,但对我来说效果很好.

速聊1:
提示:您可以使用类System.Version代替int.

I have a base class FDObjectBase which inherits from ContentControl, ISerializable, INotifyPropertyChanged. Now this class gets de-serialized using the below code logic

public FDObjectBase(SerializationInfo info, StreamingContext context) : this()
{
   Left = (double)info.GetValue("Left", typeof(double));
    Top = (double)info.GetValue("Top", typeof(double));
    Height = (double)info.GetValue("Height", typeof(double));
    Width = (double)info.GetValue("Width", typeof(double));
    DesignObjectID = (int)info.GetValue("DesignObjectID", typeof(int));
    ShapeType = (int)info.GetValue("ShapeType", typeof(int));
    Angle = (double)info.GetValue("Angle", typeof(double));
    try
    {
        ObjectType = (ObjectType)info.GetValue("ObjectType", typeof(ObjectType));
        //this.ToolTip = ObjectType.ToString();
    }
    catch { }

    OnDeserialized(new EventArgs());
    //DataObject = (DesignData)info.GetValue("DataObject", typeof(DesignData));
    //this.ToolTip = DataObject.Name + " (" + DataObject.ObjectType.ToString().ToLower() + ")";    

}

This works fine in the application however the issue is that i want the de-serialization process to be backward compatible since as per the requirement changes to the this class must not break the de-serialized data which is stored in the database. The solution which i found for this is to add the [OptionalField] attribute to all the new fields which are added to this class however this has not worked out for me. I am still getting the same exception when i add a new field this class (After adding the new field the deserialization does not happen and the below exception is thrown)

System.Reflection.TargetInvocationException was caught
  Message=Exception has been thrown by the target of an invocation.
  Source=mscorlib
  StackTrace:
       at System.RuntimeMethodHandle._SerializationInvoke(IRuntimeMethodInfo method, Object target, SignatureStruct& declaringTypeSig, SerializationInfo info, StreamingContext context)
       at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)
       at System.Runtime.Serialization.ObjectManager.FixupSpecialObject(ObjectHolder holder)
       at System.Runtime.Serialization.ObjectManager.DoFixups()
       at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
       at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
       at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
       at .SeatManagement.Client.Helpers.Generics.Deserialize[T](String data) in Generics.cs:line 117
  InnerException: System.Runtime.Serialization.SerializationException
       Message=Member 'Test' was not found.
       Source=mscorlib

So i am looking for any suggestions which are in the direction of versioning serialization c# when using iserializable interface so that it does not break even when we add new fields to the class. Thanks in advance for your help.

Solutions1

Finally i have found a solution to this .. however it does not use the out of the box features which are provided by System.Runtime.Serialization. I have added a property version on my base class

private static int VERSION_NUMBER = 1;

which gets serialized along with the other design properties as below.

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("Version", VERSION_NUMBER);
    //First Version properties
    info.AddValue("Left", Left);
    info.AddValue("Top", Top);
    info.AddValue("Height", Height);
    info.AddValue("Width", Width);
    info.AddValue("DesignObjectID", DesignObjectID);
    info.AddValue("ShapeType", ShapeType);
    info.AddValue("ObjectType", ObjectType.GetIntValue());
    info.AddValue("Angle", Angle);
    //Second Version Properties
}

Now when i deserialize the string i check the version which is stored in the deserialized string

public FDObjectBase(SerializationInfo info, StreamingContext context) : this()
{
    int version = Generics.GetSerializedValue<int>(info, "Version");

    if (version >= 1)
    {
        Left = (double)info.GetValue("Left", typeof(double));
        Top = (double)info.GetValue("Top", typeof(double));
        Height = (double)info.GetValue("Height", typeof(double));
        Width = (double)info.GetValue("Width", typeof(double));
        DesignObjectID = (int)info.GetValue("DesignObjectID", typeof(int));
        ShapeType = (int)info.GetValue("ShapeType", typeof(int));
        Angle = (double)info.GetValue("Angle", typeof(double));
        ObjectType = ((int)info.GetValue("ObjectType", typeof(int))).GetEnumValue<ObjectType>();
    }
    if (version >= 2)
    {
        //Add newly added properties for this version
    }
    else { 
        //Add default values for new properties for this version
    }

    OnDeserialized(new EventArgs());

} 

So now when i add a new property which was not deserialized earlier i just increment the version and add the new properties under the second if statement. This way the information which is stored in the DB is always backward compatible. It might not be the best solution but it works perfectly for me.

Talk1:
Tipp: You can use the class System.Version instead of an int.
转载于:https://stackoverflow.com/questions/23054513/versioning-serialization-in-c-sharp-when-using-iserializable-interface

本人是.net程序员,因为英语不行,使用工具翻译,希望对有需要的人有所帮助
如果本文质量不好,还请谅解,毕竟这些操作还是比较费时的,英语较好的可以看原文

留言回复
我们只提供高质量资源,素材,源码,坚持 下了就能用 原则,让客户花了钱觉得值
上班时间 : 周一至周五9:00-17:30 期待您的加入