Home > Blockchain >  How to add property to DTO in a backward compatible way?
How to add property to DTO in a backward compatible way?

Time:09-17

WCF is used to transfer data between clients and a server. Old DTO:

[Serializable]
public class TestClass
{
    private string firstProperty;

    [XmlAttribute]
    public string FirstProperty
    {
        get => firstProperty;
        set => firstProperty = value;
    }
}

Clients will keep sending the old version. The class needs to be extended to look like this:

[Serializable]
public class TestClass
{
    private string firstProperty;
    private string secondProperty;
    
    [XmlAttribute]
    public string FirstProperty
    {
        get => firstProperty;
        set => firstProperty = value;
    }

    [XmlAttribute]
    public string SecondProperty
    {
        get => secondProperty;
        set => secondProperty = value;
    }
}

Serialization:

public static void SerializeDataContract<T>(T obj, string path)
{
    var serializer = new DataContractSerializer(typeof(T));
    var settings = new XmlWriterSettings { Indent = true };
    using (var writer = XmlWriter.Create(path, settings))
    {
        serializer.WriteObject(writer, obj);
    }
}

public static T DeserializeDataContract<T>(string path)
{
    var serializer = new DataContractSerializer(typeof(T));
    using (var s = File.OpenRead(path))
    {
        return (T) serializer.ReadObject(s);
    }
}

The server and some clients will use the new version. If I serialize the old version and deserialize it into the new version, the following error is thrown:

System.Runtime.Serialization.SerializationException: 'Error in line 1 position 129. 'EndElement' 'TestClass' from namespace 'http://schemas.datacontract.org/2004/07/DTOs' is not expected. Expecting element 'secondProperty'.'

The exception is usually thrown in the WCF layer but I extracted a minimal reproducible example. If I use the XmlSerializer the error is gone. But changing the serializer is not an option because the old clients will keep using the DataContractSerializer.

So I'm having a hard time getting this to work because of the XmlSerializer attributes in combination with the DataContractSerializer. Any advice?

CodePudding user response:

You can indicate that secondProperty is optional by marking it with [OptionalFieldAttribute]:

[Serializable]
public class TestClass
{
    private string firstProperty;

    [OptionalField]
    private string secondProperty;

    [XmlAttribute]
    public string FirstProperty
    {
        get => firstProperty;
        set => firstProperty = value;
    }

    [XmlAttribute]
    public string SecondProperty
    {
        get => secondProperty;
        set => secondProperty = value;
    }
}

When a type is marked with [Serializable] but not data contract attributes, the data contract serializer will serialize the public and private fields of instances of the type -- not the properties -- in a manner similar to BinaryFormatter. As such all fields are required to be present unless marked with [OptionalField].

For more see Types Supported by the Data Contract Serializer and Version tolerant serialization: Tolerance of missing data.

Notes:

Demo fiddle here.

  • Related