如何以向后兼容的方式将 属性 添加到 DTO?

How to add property to DTO in a backward compatible way?

WCF 用于在客户端和服务器之间传输数据。旧 DTO:

[Serializable]
public class TestClass
{
    private string firstProperty;

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

客户将继续发送旧版本。 class 需要扩展为如下所示:

[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;
    }
}

序列化:

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);
    }
}

服务器和部分客户端将使用新版本。如果我将旧版本序列化并反序列化为新版本,则会抛出以下错误:

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

异常通常在 WCF 层中引发,但我提取了一个最小的可重现示例。 如果我使用 XmlSerializer,错误就消失了。但是更改序列化程序不是一种选择,因为旧客户端将继续使用 DataContractSerializer。

由于 XmlSerializer 属性与 DataContractSerializer 的结合,我很难让它工作。有什么建议吗?

您可以通过用 [OptionalFieldAttribute] 标记来表明 secondProperty 是可选的:

[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;
    }
}

当类型标记为 [Serializable] but not data contract attributes 时,数据协定序列化程序将序列化该类型实例的 public 和私有 字段 -- 不是属性 - 以类似于 BinaryFormatter 的方式。因此,除非标有 [OptionalField].

,否则所有字段都必须存在

有关更多信息,请参阅 Types Supported by the Data Contract Serializer and Version tolerant serialization: Tolerance of missing data

备注:

  • 要将自动实现的 属性 的支持字段标记为可选,请参阅 NetDataContractSerializer Deserialization With New Property

    也就是说,我不建议使用 [Serializable] 类型的自动属性,因为序列化流将包含秘密支持字段的名称。有关详细信息,请参阅 .NET WebAPI Serialization k_BackingField Nastiness

  • 您已用 [XmlAttribute], but this attribute is ignored by DataContractSerializer. It only affects serialization by XmlSerializer 标记您的类型。

演示 fiddle here.