本文介绍了对于protobuf网,版本控制和代理各类最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我试图确定如何使用protobuf网(马克Gravell的实现)来解决这个用例。 我们上课A,这被认为是版本1 A类的一个实例已序列化到磁盘 我们现在有b类,这被认为是第2版A级(有这么多的东西错了A级,我们必须为下一个版本创建B类)。 A类仍然存在代码,但对遗留目的 我要反序列化的版本:1。数据(存储到磁盘)为B类的实例,并使用逻辑例程从以前的A类的实例转换数据,以b级的新实例。 b类的实例将操作过程中被序列化到磁盘中。 应用程序应该预料到反序列化既类和b类的实例。 数据合同代理人的概念和DataContractSerializer的来心神。我们的目标是过渡版本:1的数据到新的B类结构 一个例子: [DataContract] 公共类A { 公开发行A(){} [数据成员] 公共BOOL IsActive {获取;设置] [数据成员] 公众诠释VERSIONNUMBER { {返回1; } 集合{} } [数据成员] 公众诠释TimeInSeconds {获取;集;} [数据成员] 公共字符串名称{;设置;} [数据成员] 公共CustomObject CustomObj {获取;设置;} //另外一个DataContract [数据成员] 公开名单< ComplexThing> ComplexThings {获取;集;} //另外一个DataContract :} [DataContract] 公共类B { 酒店的公共b(A中){ this.Enabled = a.IsActive; //属性现在有一个不同的名称 this.TimeInMilliseconds = a.TimeInSeconds * 1000; //属性需要数学的正确性 this.Name = a.Name; this.CustomObject2 =新CustomObject2(a.CustomObj); //引用对象的变化,太 this.ComplexThings =新的List< ComplexThings>(); this.ComplexThings.AddRange(a.ComplexThings); :} 公众B(){} [数据成员] 公共BOOL启用{获取;集;] [数据成员] 公众诠释版本{得到{2; } 集合{} } [数据成员] 公共双TimeInMilliseconds {获取;集;} [数据成员] 公共字符串名称{;设置;} [数据成员] 公共CustomObject2 CustomObject {获取;设置;} //另外一个DataContract [数据成员] 公开名单< ComplexThing> ComplexThings {获取;集;} //另外一个DataContract :} A级是我们的对象的第一次迭代,并积极使用。数据存在于V1格式,使用A类进行序列化。 实现我们的方法错误后,我们创建一个名为B类新的结构之间存在着这么多的变化A和b,我们觉得这是更好地创造b,而不是适应原来的A级。 但我们的应用程序已经存在,并且A级被用来序列数据。我们已经准备好推出我们改变了世界,但我们必须先在反序列化版本1(使用A类)创建的数据和实例作为B级的数据是不够显著,我们不能想当然地认为在课堂上的默认值B中丢失的数据,而是我们必须从A类的实例一旦我们有一个B类的实例转换数据,以B类,应用程序将在B类格式(第2版)再次序列化数据。 我们假设,我们将在今后做出修改,以b级的,我们希望能够迭代到第3版,也许在一类新的C。我们有两个目标:地址数据已经存在,并为未来的迁移做好准备我们的对象 现有的过渡属性(OnSerializing / OnSerialized,OnDeserializing / OnDeserialized等。 。)不提供访问以前的数据。 在这种情况下使用protobuf网时,什么是预期的做法呢? 解决方案 右键;看着他们,你的确彻底改变了合同。我知道,是要爱你,没有基于合同的序列化和protobuf网是没有什么不同。如果你已经有了一个根节点,你的可能的做这样的事情(在伪代码): [合同] 类包装{ [会员]公共AA {获取;集;} [会员]公开BB {获取;集;} [会员]大众CC {获取;集;} } 和随便挑取其A / b / C的非空,也许在他们之间添加一些转换操作符。不过,如果你只是有一个赤身裸体的旧数据,这变得很难。还有我能想到的两种方法: 添加的地段的兼容性垫片性能; 的不的非常维护,我不推荐 嗅出版本作为第一步,告诉什么期望串行 例如,你可以这样做: INT版本= -1;使用(VAR读卡器=新ProtoReader(的InputStream)){,而(reader.ReadFieldHeader()0){ const int的VERSION_FIELD_NUMBER = / * TODO * /; 如果(reader.FieldNumber == VERSION_FIELD_NUMBER){版本= reader.ReadInt32(); //可选的短路;我们并不希望2版本号中断; }其他{ reader.SkipField(); } } } inputStream.Position = 0; //反序列化之前倒带 现在可以使用序列化,告诉它什么版本它被序列化为;无论是通过普通的 Serializer.Deserialize< T> API,或通过键入由两个非一般API 实例( Serializer.NonGeneric.Deserialize 或 RuntimeTypeModel.Default.Deserialize - 无论哪种方式,你到了同一个地方,它真是是否通用或非泛型是最方便的)的情况。 那么你就需要 A / B / C - 无论是通过自己的运营商定制/方法,或者通过类似自动映射器 如果您不希望任何 ProtoReader 代码踢身边,你也可以这样做: [DataContract] 类VersionStub { [数据成员(订单= VERSION_FIELD_NUMBER)] 公众诠释版本{获取;设置;} } 和运行反序列化< VersionStub> ; ,这将给你访问版本,然后你就可以用它来执行特定类型的反序列化;这里的主要区别是, ProtoReader 代码,只要你有一个版本号,您可以短路。 I'm trying to determine how to address this use case using protobuf-net (Marc Gravell's implementation).We have class A, which is considered version 1An instance of class A has been serialized to diskWe now have class B, which is considered version 2 of class A (there were so many things wrong with class A, we had to create class B for the next version). Class A still exists in code, but only for legacy purposes.I want to deserialize the version:1 data (stored to disk) as a class B instance, and use a logic routine to translate the data from the previous class A instance to a new instance of class B.The instance of class B will be serialized to disk during operation.The application should expect to deserialize instances of both class A and B.The concept of data contract surrogates and the DataContractSerializer come to mind. The goal is transition the version:1 data to the new class B structure.An example:[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 ...}Class A was the first iteration of our object, and is actively in use. Data exists in v1 format, using class A for serialization.After realizing the error of our ways, we create a new structure called class B. There are so many changes between A and B that we feel it's better to create B, as opposed to adapting the original class A.But our application already exists and class A is being used to serialize data. We're ready to roll our changes out to the world, but we must first deserialize data created under version 1 (using class A) and instantiate it as class B. The data is significant enough that we can't just assume defaults in class B for missing data, but rather we must transition the data from a class A instance to class B. Once we have a class B instance, the application will serialize that data again in class B format (version 2).We're assuming we'll make modifications to class B in the future, and we want to be able to iterate to a version 3, perhaps in a new class "C". We have two goals: address data already in existence, and prepare our objects for future migration.The existing "transition" attributes (OnSerializing/OnSerialized,OnDeserializing/OnDeserialized,etc.) don't provide access to the previous data.What is the expected practice when using protobuf-net in this scenario? 解决方案 Right; looking at them you have indeed completely changed the contract. I know of no contract-based serializer that is going to love you for that, and protobuf-net is no different. If you already had a root node, you could do something like (in pseudo-code):[Contract]class Wrapper { [Member] public A A {get;set;} [Member] public B B {get;set;} [Member] public C C {get;set;}}and just pick whichever of A/B/C is non-null, perhaps adding some conversion operators between them. However, if you just have a naked A in the old data, this gets hard. There are two approaches I can think of:add lots of shim properties for compatibility; not very maintainable, and I don't recommend itsniff the Version as an initial step, and tell the serializer what to expect.For example, you could do: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 deserializingNow you can use the serializer, telling it what version it was serialized as; either via the generic Serializer.Deserialize<T> API, or via a Type instance from the two non-generic APIs (Serializer.NonGeneric.Deserialize or RuntimeTypeModel.Default.Deserialize - either way, you get to the same place; it is really a case of whether generic or non-generic is most convenient).Then you would need some conversion code between A / B / C - either via your own custom operators / methods, or by something like auto-mapper.If you don't want any ProtoReader code kicking around, you could also do:[DataContract]class VersionStub { [DataMember(Order=VERSION_FIELD_NUMBER)] public int Version {get;set;}}and run Deserialize<VersionStub>, which will give you access to the Version, which you can then use to do the type-specific deserialize; the main difference here is that the ProtoReader code allows you to short-circuit as soon as you have a version-number. 这篇关于对于protobuf网,版本控制和代理各类最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-01 06:28