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

Posted

技术标签:

【中文标题】如何以向后兼容的方式向 DTO 添加属性?【英文标题】:How to add property to DTO in a backward compatible way? 【发布时间】:2021-10-10 18:34:33 【问题描述】:

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

[Serializable]
public class TestClass

    private string firstProperty;

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

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

[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:'第 1 行位置 129 中的错误。'EndElement''TestClass' 来自命名空间'http://schemas.datacontract.org/2004/07/DTOs' 不是预期的。期望元素“secondProperty”。'

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

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

【问题讨论】:

试试 OptionalFieldAttribute 类,它指定一个字段可以从序列化流中丢失,这样 BinaryFormatter 和 SoapFormatter 就不会抛出异常。 告诉您这些可能为时已晚,但您在应用程序中使用的“数据绑定”方法非常适合简单稳定的 XML 模式,但当模式开始发生变化时就会成为一场噩梦。 【参考方案1】:

您可以通过标记[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] 而不是data contract attributes 时,数据协定序列化程序将序列化该类型实例的公共和私有字段——而不是属性——在一个方式类似于BinaryFormatter。因此,除非标有[OptionalField],否则所有字段都必须存在。

如需了解更多信息,请参阅 Types Supported by the Data Contract SerializerVersion tolerant serialization: Tolerance of missing data

注意事项:

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

话虽如此,我不建议使用 [Serializable] 类型的自动属性,因为序列化流将包含秘密支持字段的名称。详情请见.NET WebAPI Serialization k_BackingField Nastiness

您已用[XmlAttribute] 标记了您的类型,但DataContractSerializer 忽略了此属性。它只影响XmlSerializer的序列化。

演示小提琴here.

【讨论】:

以上是关于如何以向后兼容的方式向 DTO 添加属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何对前端项目进行版本控制?

HTTP Restful 语义版本控制

如何在不破坏向后兼容性的情况下更改 DataContract 属性的类型?

package.json中 版本号详解

我可以添加具有向后兼容性的 [FromBody] 参数吗?

Xcode 7 Stack Views 如何向后兼容 iOS 8 和 iOS 7?