Serialization and versioning

11,859

Solution 1

I strongly advocate against storing BinaryFormatter or SoapFormatter data in the database; it is:

  • brittle
  • not version tolerant
  • not platform independent

BinaryFormatter is OK for data transfer between .NET assemblies (at a push), but I would recommend a more predictable serializer. DataContractSerializer is an option (as is JSON or XmlSerializer), but I would not use NetDataContractSerializer for all the same reasons above. I would be tempted to use protobuf-net, since that is efficient binary in a known efficient format, platform independent and version tolerant!

For example:

[DataContract]
public class SerializableContingentOrder
{
    [DataMember(Order=1)] public Guid SomeGuidData { get; set; }
    [DataMember(Order=2)] public decimal SomeDecimalData { get; set; }
    [DataMember(Order=3)] public MyEnumerationType1 EnumData1 { get; set; }
}

Serialization:

protected override string Serialize()
{
    SerializableContingentOrder sco = new SerializableContingentOrder(this);   
    using(MemoryStream ms = new MemoryStream()) {
        Serializer.Serialize(ms, sco);
        return Convert.ToBase64String(ms.ToArray());
    }
}

Deserialization:

protected override bool Deserialize(string data)
{
    using(MemoryStream ms = new MemoryStream(Convert.FromBase64String(data)) {
        SerializableContingentOrder sco =
               Serializer.Deserialize<SerializableContingentOrder>(ms)
    }
    return true;
}

Solution 2

You have two options if you want to support versioning. Use DataContracts or use Version Tolerant Serialization. Both are valid.

DataContacts handle the addition and removal of fields automatically. See Data Contact Versioning and Best Practices: Data Contract Versioning for more information.

DataContact Example

[DataContract]
public class ContingentOrder
{
    [DataMember(Order=1)]
    public Guid TriggerDealAssetID;

    [DataMember(Order=2)]
    public decimal TriggerPrice;

    [DataMember(Order=3)]
    public TriggerPriceTypes TriggerPriceType;

    [DataMember(Order=4)]
    public PriceTriggeringConditions PriceTriggeringCondition;

}

Version Tolerant Serialization Example

// Version 1
[Serializable]
public class SerializableContingentOrder
{
    public Guid TriggerDealAssetID;
    public decimal TriggerPrice;
    public TriggerPriceTypes TriggerPriceType;
    // Omitted PriceTriggeringCondition as an example
}

// Version 2 
[Serializable]
public class SerializableContingentOrder
{
    public Guid TriggerDealAssetID;
    public decimal TriggerPrice;
    public TriggerPriceTypes TriggerPriceType;

    [OptionalField(VersionAdded = 2)]
    public PriceTriggeringConditions PriceTriggeringCondition;

    [OnDeserializing]
    void SetCountryRegionDefault (StreamingContext sc)
    {
        PriceTriggeringCondition = /* DEFAULT VALUE */;
    }

}

More information on Version Tolerant Serialization (the link was broken). Especially take note of the best-practices at the bottom of the page.

Note, DataContracts were introduced in .NET 3.5 so you may not have that option if you need to target .NET 2.0.

HTH,

Solution 3

Since .NET 2.0 you have Version Tolerant Serialization support if you use BinaryFormatter. The SoapFormatter also supports some version tolerant features but not all the ones supported by the BinaryFormatter, more specifically extraneous data tolerance is not supported.

For more information you should check:

Version Tolerant Serialization

Solution 4

The simplest way is to decorate new fields with the OptionalFieldAttribute. It's not perfect but it might do in your case.

Share:
11,859
Captain Comic
Author by

Captain Comic

I am interested in C#, .NET, algorithms, and finance.

Updated on June 04, 2022

Comments

  • Captain Comic
    Captain Comic almost 2 years

    I need to serialize some data to string. The string is then stored in DB in a special column SerializeData.

    I have created special classes that are used for serialization.

    [Serializable]
    public class SerializableContingentOrder
    {
        public Guid SomeGuidData { get; set; }
        public decimal SomeDecimalData { get; set; }
        public MyEnumerationType1 EnumData1 { get; set; }
    }
    

    Serialization:

    protected override string Serialize()
    {
        SerializableContingentOrder sco = new SerializableContingentOrder(this);
    
        MemoryStream ms = new MemoryStream();
        SoapFormatter sf = new SoapFormatter();
        sf.Serialize(ms, sco);
        string data = Convert.ToBase64String(ms.ToArray());
        ms.Close();
        return data;
    }
    

    Deserialization:

    protected override bool Deserialize(string data)
    {
        MemoryStream ms = new MemoryStream(Convert.FromBase64String(data).ToArray());
        SoapFormatter sf = new SoapFormatter();
    
        SerializableContingentOrder sco = sf.Deserialize(ms) as SerializableContingentOrder;
        ms.Close();
        return true;
    }
    

    Now I want to have versioning support. What happens if I change SerializableContingentOrder class. I want to be able to add new fields in the future.

    Do I have to switch to DataContract serialization? Please give me short snippet?