views:

71

answers:

4

Hi,

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?

+3  A: 

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

João Angelo
In my experience, the `BinaryFormatter` "version tolerant serialization" simply *isn't* .
Marc Gravell
@Marc Gravell, I'll take your word for it, but could you elaborate on the specific problems you faced?
João Angelo
Agree with Marc, VTS allows for some changes in the code, like adding an optional field, but it is very hard to be sure that all subsequent changes will be "VTS-compatible".
VladV
+1  A: 

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

Phil
+4  A: 

I strongly advocate against not 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;
}
Marc Gravell
1) What is Serializer.Serialize()? Which namespace/assemply does it belong? 2) Thank you for such comprehensive answer, but how do you mean"BinaryFormatter or SoapFormatter is brittle"? Plese expand a bit
Captain Comic
Shouldn't you be advocating against storing instead of *not* storing? :) Don't get this the wrong way, English is not my native language, hence the question.
João Angelo
@Captain Comic - `Serializer` is (per my reply) protobuf-net. Re "not storing", it is correct as written. I **do not** recommend `BinaryFormatter` for storage purposes. Re brittle: http://marcgravell.blogspot.com/2009/03/obfuscation-serialization-and.html
Marc Gravell
+2  A: 

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. 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,

Dennis Roche