gpt4 book ai didi

c# - protobuf-net、版本控制和代理类型的最佳实践

转载 作者:太空狗 更新时间:2023-10-29 23:16:09 28 4
gpt4 key购买 nike

我正在尝试确定如何使用 protobuf-net(Marc Gravell 的实现)来解决这个用例。

  • 我们有 A 类,它被认为是版本 1
  • 类A的一个实例已经序列化到磁盘
  • 我们现在有了 B 类,它被认为是 A 类的版本 2(A 类有很多问题,我们不得不为下一个版本创建 B 类)。 A 类仍然存在于代码中,但仅用于遗留目的。
  • 我想将 version:1 数据(存储到磁盘)反序列化为 B 类实例,并使用逻辑例程将数据从先前的 A 类实例转换为 B 类的新实例。
  • B类实例在运行时会序列化到磁盘。
  • 应用程序应该期望反序列化 A 类和 B 类的实例。

我想到了数据协定代理和 DataContractSerializer 的概念。目标是将 version:1 数据转换为新的 B 类结构。

一个例子:

[DataContract]
public class A {

public A(){}

[DataMember]
public bool IsActive {get;set;]

[DataMember]
public int VersionNumber {
get { return 1; }
set { }
}

[DataMember]
public int TimeInSeconds {get;set;}

[DataMember]
public string Name {get;set;}

[DataMember]
public CustomObject CustomObj {get;set;} //Also a DataContract

[DataMember]
public List<ComplexThing> ComplexThings {get;set;} //Also a DataContract
...
}

[DataContract]
public class B {

public B(A a) {
this.Enabled = a.IsActive; //Property now has a different name
this.TimeInMilliseconds = a.TimeInSeconds * 1000; //Property requires math for correctness
this.Name = a.Name;
this.CustomObject2 = new CustomObject2(a.CustomObj); //Reference objects change, too
this.ComplexThings = new List<ComplexThings>();
this.ComplexThings.AddRange(a.ComplexThings);
...
}

public B(){}

[DataMember]
public bool Enabled {get;set;]

[DataMember]
public int Version {
get { return 2; }
set { }
}

[DataMember]
public double TimeInMilliseconds {get;set;}

[DataMember]
public string Name {get;set;}

[DataMember]
public CustomObject2 CustomObject {get;set;} //Also a DataContract

[DataMember]
public List<ComplexThing> ComplexThings {get;set;} //Also a DataContract
...
}

A 类是我们对象的第一次迭代,并且正在积极使用中。数据以 v1 格式存在,使用类 A 进行序列化。

在意识到我们的方法错误之后,我们创建了一个名为 B 类的新结构。A 和 B 之间有很多变化,我们觉得创建 B 比修改原始类 A 更好。

但是我们的应用程序已经存在并且类 A 正在用于序列化数据。我们已准备好将我们的更改推向世界,但我们必须首先反序列化在版本 1(使用类 A)下创建的数据并将其实例化为类 B。数据足够重要,我们不能只假设类中的默认值B 用于丢失数据,而是我们必须将数据从 A 类实例转移到 B 类。一旦我们有了 B 类实例,应用程序将再次以 B 类格式(版本 2)序列化该数据。

我们假设我们将来会对 B 类进行修改,并且我们希望能够迭代到版本 3,也许是在一个新的类“C”中。我们有两个目标:处理已经存在的数据,并为 future 的迁移准备我们的对象。

现有的“转换”属性(OnSerializing/OnSerialized、OnDeserializing/OnDeserialized 等)不提供对以前数据的访问。

在这种情况下使用 protobuf-net 的预期做法是什么?

最佳答案

对;看着他们,你确实完全改变了契约(Contract)。我知道没有任何基于契约的序列化器会因此爱上你,protobuf-net 也不异常(exception)。如果您已经有一个根节点,您可以做类似的事情(在伪代码中):

[Contract]
class Wrapper {
[Member] public A A {get;set;}
[Member] public B B {get;set;}
[Member] public C C {get;set;}
}

然后只选择 A/B/C 中非空的那个,也许在它们之间添加一些转换运算符。但是,如果您在旧数据中只有一个裸 A,这就会变得困难。我可以想到两种方法:

  • 添加 很多 垫片属性以实现兼容性; 不是非常易于维护,我不推荐
  • 嗅探Version作为初始步骤,并告诉序列化程序期望什么。

例如,你可以这样做:

int version = -1;
using(var reader = new ProtoReader(inputStream)) {
while(reader.ReadFieldHeader() > 0) {
const int VERSION_FIELD_NUMBER = /* todo */;
if(reader.FieldNumber == VERSION_FIELD_NUMBER) {
version = reader.ReadInt32();
// optional short-circuit; we're not expecting 2 Version numbers
break;
} else {
reader.SkipField();
}
}
}
inputStream.Position = 0; // rewind before deserializing

现在你可以使用序列化器,告诉它什么version它被序列化为;通过通用 Serializer.Deserialize<T> API,或通过 Type来自两个非通用 API 的实例(Serializer.NonGeneric.DeserializeRuntimeTypeModel.Default.Deserialize - 无论哪种方式,您都会到达同一个地方;这实际上是通用或非通用最方便的情况)。

那么你需要一些 A 之间的转换代码/B/C - 通过您自己的自定义运算符/方法,或通过自动映射器之类的东西。

如果你不想要任何 ProtoReader代码踢,你也可以这样做:

[DataContract]
class VersionStub {
[DataMember(Order=VERSION_FIELD_NUMBER)]
public int Version {get;set;}
}

然后运行Deserialize<VersionStub> , 这将使您可以访问 Version ,然后您可以使用它来执行特定于类型的反序列化;这里的主要区别在于 ProtoReader代码允许您在获得版本号后立即短路。

关于c# - protobuf-net、版本控制和代理类型的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14491140/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com