【发布时间】:2012-01-21 02:34:11
【问题描述】:
协议缓冲区如何处理类型版本控制?
例如,当我需要随时间更改类型定义时?比如添加和删除字段。
【问题讨论】:
标签: protocol-buffers protobuf-net
协议缓冲区如何处理类型版本控制?
例如,当我需要随时间更改类型定义时?比如添加和删除字段。
【问题讨论】:
标签: protocol-buffers protobuf-net
Google 设计的 protobuf 对版本控制非常宽容:
然而:
不过,一般来说 - 它可以正常工作,而且您不必太担心版本控制。
【讨论】:
我知道这是一个老问题,但我最近遇到了这个问题。我解决它的方法是使用外观和运行时决策来序列化。通过这种方式,我可以将字段弃用/升级为新类型,并让新旧消息优雅地处理它。
我正在使用 Marc Gravell 的 protobuf.net (v2.3.5) 和 C#,但外观理论适用于任何语言和 Google 的原始 protobuf 实现。
我的旧班级有一个 DateTime 时间戳,我想将其更改为包含“种类”(.NET 时代错误)。有效地添加它意味着它序列化为 9 个字节而不是 8 个字节,这将是一个破坏性的序列化更改!
[ProtoMember(3, Name = "Timestamp")]
public DateTime Timestamp { get; set; }
protobuf 的基本原则是永远不要更改 proto id!我想阅读旧的序列化二进制文件,这意味着“3”将继续存在。
所以,
我重命名了旧属性并将其设为私有(是的,它仍然可以通过反射魔法反序列化),但我的 API 不再显示它可用!
[ProtoMember(3, Name = "Timestamp-v1")]
private DateTime __Timestamp_v1 = DateTime.MinValue;
我创建了一个新的 Timestamp 属性,带有一个新的 proto id,并包含 DateTime.Kind
[ProtoMember(30002, Name = "Timestamp", DataFormat = ProtoBuf.DataFormat.WellKnown)]
public DateTime Timestamp { get; set; }
在旧消息的情况下,我添加了一个“AfterDeserialization”方法来更新我们的新时间
[ProtoAfterDeserialization]
private void AfterDeserialization()
{
//V2 Timestamp includes a "kind" - we will stop using __Timestamp - so keep it up to date
if (__Timestamp_v1 != DateTime.MinValue)
{
//Assume the timestamp was in UTC - as it was...
Timestamp = new DateTime(__Timestamp_v1.Ticks, DateTimeKind.Utc) //This is for old messages - we'll update our V2 timestamp...
}
}
现在,我可以正确序列化/反序列化新旧消息,并且我的时间戳现在包括 DateTime.Kind!什么都没有坏。
但是,这确实意味着这两个字段都将出现在所有新消息中。所以最后一点是使用运行时序列化决策来排除旧的时间戳(注意,如果它使用 protobuf 的 required 属性,这将不起作用!!!)
bool ShouldSerialize__Timestamp_v1()
{
return __Timestamp_v1 != DateTime.MinValue;
}
就是这样。我有一个很好的单元测试,如果有人想要它,它可以从端到端进行......
我知道我的方法依赖于 .NET 魔法,但我认为这个概念可以翻译成其他语言......
【讨论】: