Prática recomendada para tipos protobuf-net, versioning e surrogate
Estou tentando determinar como endereçar este caso de uso usando protobuf-net (implementação de Marc Gravell).
Nós temos a classe A, que é considerada a versão 1Uma instância da classe A foi serializada para discoAgora temos a classe B, que é considerada a versão 2 da classe A (havia tantas coisas erradas na classe A que tivemos que criar a classe B para a próxima versão). A classe A ainda existe no código, mas apenas para fins de legado.Deserialize a versão: 1 dados (armazenados em disco) como uma instância de classe B e use uma rotina lógica para converter os dados da instância anterior da classe A para uma nova instância da classe B.A instância da classe B será serializada em disco durante a operação.O aplicativo deve esperar desserializar instâncias de ambas as classes A e B.O conceito de substitutos de contrato de dados e o DataContractSerializer vêm à mente. O objetivo é fazer a transição dos dados da versão: 1 para a nova estrutura da classe B.
Um exemplo:
[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 classe A foi a primeira iteração do nosso objeto e está ativamente em uso. Os dados existem no formato v1, usando a classe A para serialização.
Depois de perceber o erro de nossos caminhos, criamos uma nova estrutura chamada classe B. Há tantas mudanças entre A e B que achamos melhor criar B, em vez de adaptar a classe original A.
Mas nosso aplicativo já existe e a classe A está sendo usada para serializar dados. Estamos prontos para lançar nossas mudanças para o mundo, mas primeiro precisamos desserializar os dados criados na versão 1 (usando a classe A) e instanciá-los como classe B. Os dados são significativos o suficiente para não podermos simplesmente assumir os padrões da classe. B para dados perdidos, mas precisamos fazer a transição dos dados de uma instância de classe A para a classe B. Quando tivermos uma instância de classe B, o aplicativo serializará esses dados novamente no formato de classe B (versão 2).
Estamos assumindo que faremos modificações na classe B no futuro e queremos poder fazer uma iteração para uma versão 3, talvez em uma nova classe "C". Temos dois objetivos: endereçar dados já existentes e preparar nossos objetos para migração futura.
Os atributos "transição" existentes (OnSerializing / OnSerialized, OnDeserializing / OnDeserialized, etc.) Não fornecem acesso aos dados anteriores.
Qual é a prática esperada ao usar o protobuf-net nesse cenário?