Práticas recomendadas: Controle de versão de contrato de dados
Este tópico lista as práticas recomendadas para a criação de contratos de dados que podem evoluir facilmente ao longo do tempo. Para obter mais informações sobre contratos de dados, consulte os tópicos em Usando contratos de dados.
Nota sobre a validação do esquema
Ao discutir o controle de versão de contrato de dados, é importante observar que o esquema de contrato de dados exportado pelo Windows Communication Foundation (WCF) não tem nenhum suporte a controle de versão, além do fato de que os elementos são marcados como opcionais por padrão.
Isso significa que mesmo o cenário de controle de versão mais comum, como a adição de um novo membro de dados, não pode ser implementado de uma forma que seja perfeita em relação a um determinado esquema. As versões mais recentes de um contrato de dados (com um novo membro de dados, por exemplo) não são validadas usando o esquema antigo.
No entanto, há muitos cenários em que a conformidade estrita do esquema não é necessária. Muitas plataformas de serviços Web, incluindo WCF e XML Web Services criados usando ASP.NET, não executam a validação de esquema por padrão e, portanto, toleram elementos extras que não são descritos pelo esquema. Ao trabalhar com essas plataformas, muitos cenários de controle de versão são mais fáceis de implementar.
Assim, há dois conjuntos de diretrizes de controle de versão de contrato de dados: um conjunto para cenários em que a validade estrita do esquema é importante e outro conjunto para cenários em que não é.
Controle de versão quando a validação do esquema é necessária
Se for necessária uma validade de esquema estrita em todas as direções (do novo para o antigo e do antigo para o novo), os contratos de dados devem ser considerados imutáveis. Se o controle de versão for necessário, um novo contrato de dados deve ser criado, com um nome ou namespace diferente, e o contrato de serviço usando o tipo de dados deve ser versionado de acordo.
Por exemplo, um contrato de serviço de processamento de ordem de compra nomeado PoProcessing
com uma PostPurchaseOrder
operação usa um parâmetro que está em conformidade com um PurchaseOrder
contrato de dados. Se o PurchaseOrder
contrato tiver que mudar, você deve criar um novo contrato de dados, ou seja, PurchaseOrder2
que inclui as alterações. Em seguida, você deve lidar com o controle de versão no nível do contrato de serviço. Por exemplo, criando uma PostPurchaseOrder2
operação que usa o PurchaseOrder2
parâmetro ou criando um PoProcessing2
contrato de serviço onde a PostPurchaseOrder
operação usa um contrato de PurchaseOrder2
dados.
Observe que as alterações nos contratos de dados que são referenciadas por outros contratos de dados também se estendem à camada do modelo de serviço. Por exemplo, no cenário anterior, o PurchaseOrder
contrato de dados não precisa ser alterado. No entanto, ele contém um membro de dados de um Customer
contrato de dados, que, por sua vez, continha um membro de dados do Address
contrato de dados, que precisa ser alterado. Nesse caso, você precisaria criar um contrato de Address2
dados com as alterações necessárias, um contrato de Customer2
dados que contenha o Address2
membro de dados e um contrato de PurchaseOrder2
dados que contenha um Customer2
membro de dados. Tal como no caso anterior, o contrato de prestação de serviços também teria de ser versionado.
Embora nesses exemplos os nomes sejam alterados (anexando um "2"), a recomendação é alterar namespaces em vez de nomes, anexando novos namespaces com um número de versão ou uma data. Por exemplo, o contrato de http://schemas.contoso.com/2005/05/21/PurchaseOrder
dados mudaria para o http://schemas.contoso.com/2005/10/14/PurchaseOrder
contrato de dados.
Para obter mais informações, consulte Práticas recomendadas: controle de versão de serviço.
Ocasionalmente, você deve garantir a estrita conformidade do esquema para mensagens enviadas pelo seu aplicativo, mas não pode confiar que as mensagens recebidas sejam estritamente compatíveis com o esquema. Neste caso, existe o perigo de uma mensagem recebida poder conter dados estranhos. Os valores estranhos são armazenados e retornados pelo WCF e, portanto, resulta no envio de mensagens inválidas do esquema. Para evitar esse problema, o recurso de ida e volta deve ser desativado. Pode fazê-lo de duas formas.
Não implemente a IExtensibleDataObject interface em nenhum dos seus tipos.
Aplique um ServiceBehaviorAttribute atributo ao seu contrato de serviço com a IgnoreExtensionDataObject propriedade definida como
true
.
Para obter mais informações sobre round-tripping, consulte Forward-Compatible Data Contracts.
Controle de versão quando a validação do esquema não é necessária
A estrita conformidade do esquema raramente é necessária. Muitas plataformas toleram elementos extras não descritos por um esquema. Desde que isso seja tolerado, o conjunto completo de recursos descritos em Controle de versão de contrato de dados e Contratos de dados compatíveis com encaminhamento podem ser usados. Recomendam-se as seguintes orientações.
Algumas das diretrizes devem ser seguidas exatamente para enviar novas versões de um tipo onde uma mais antiga é esperada ou enviar uma antiga onde a nova é esperada. Outras diretrizes não são estritamente necessárias, mas estão listadas aqui porque podem ser afetadas pelo futuro do controle de versão do esquema.
Não tente contratos de dados de versão por tipo de herança. Para criar versões posteriores, altere o contrato de dados em um tipo existente ou crie um novo tipo não relacionado.
O uso de herança juntamente com contratos de dados é permitido, desde que a herança não seja usada como um mecanismo de controle de versão e que certas regras sejam seguidas. Se um tipo derivar de um determinado tipo de base, não o faça derivar de um tipo de base diferente em uma versão futura (a menos que tenha o mesmo contrato de dados). Há uma exceção a isso: você pode inserir um tipo na hierarquia entre um tipo de contrato de dados e seu tipo base, mas somente se ele não contiver membros de dados com os mesmos nomes que outros membros em quaisquer versões possíveis dos outros tipos na hierarquia. Em geral, o uso de membros de dados com os mesmos nomes em diferentes níveis da mesma hierarquia de herança pode levar a sérios problemas de controle de versão e deve ser evitado.
A partir da primeira versão de um contrato de dados, implemente IExtensibleDataObject sempre para permitir o round-tripping. Para obter mais informações, consulte Contratos de dados compatíveis com encaminhamento. Se você lançou uma ou mais versões de um tipo sem implementar essa interface, implemente-a na próxima versão do tipo.
Em versões posteriores, não altere o nome do contrato de dados ou namespace. Se alterar o nome ou namespace do tipo subjacente ao contrato de dados, certifique-se de preservar o nome do contrato de dados e o namespace usando os mecanismos apropriados, como a Name propriedade do DataContractAttribute. Para obter mais informações sobre nomenclatura, consulte Nomes de contratos de dados.
Em versões posteriores, não altere os nomes de nenhum membro de dados. Se alterar o nome do campo, propriedade ou evento subjacente ao membro de dados, use a
Name
propriedade do para preservar o nome do membro de DataMemberAttribute dados existente.Em versões posteriores, não altere o tipo de qualquer campo, propriedade ou evento subjacente a um membro de dados de forma que o contrato de dados resultante para esse membro de dados seja alterado. Tenha em mente que os tipos de interface são equivalentes para Object fins de determinação do contrato de dados esperado.
Em versões posteriores, não altere a ordem dos membros de dados existentes ajustando a OrderDataMemberAttribute propriedade do atributo.
Em versões posteriores, novos membros de dados podem ser adicionados. Devem sempre seguir estas regras:
A IsRequired propriedade deve sempre ser deixada em seu valor padrão de
false
.Se um valor padrão de ou zero para o membro for inaceitável, um método de retorno de
null
chamada deve ser fornecido usando o OnDeserializingAttribute para fornecer um padrão razoável no caso de o membro não estar presente no fluxo de entrada. Para obter mais informações sobre o retorno de chamada, consulte Retornos de chamada de serialização tolerantes à versão.A DataMemberAttribute.Order propriedade deve ser usada para garantir que todos os membros de dados recém-adicionados apareçam após os membros de dados existentes. A maneira recomendada de fazer isso é a seguinte: Nenhum dos membros de dados na primeira versão do contrato de dados deve ter sua
Order
propriedade definida. Todos os membros de dados adicionados na versão 2 do contrato de dados devem ter suaOrder
propriedade definida como 2. Todos os membros de dados adicionados na versão 3 do contrato de dados devem ter suaOrder
definição como 3 e assim por diante. É permitido ter mais de um membro de dados definido para o mesmoOrder
número.
Não remova membros de dados em versões posteriores, mesmo que a IsRequired propriedade tenha sido deixada em sua propriedade padrão de
false
versões anteriores.Não altere a
IsRequired
propriedade em nenhum membro de dados existente de versão para versão.Para membros de dados necessários (onde
IsRequired
estátrue
), não altere aEmitDefaultValue
propriedade de versão para versão.Não tente criar hierarquias de controle de versão ramificadas. Ou seja, sempre deve haver um caminho em pelo menos uma direção de qualquer versão para qualquer outra versão usando apenas as alterações permitidas por essas diretrizes.
Por exemplo, se a versão 1 de um contrato de dados de Pessoa contiver apenas o membro de dados Nome, você não deverá criar a versão 2a do contrato adicionando apenas o membro Idade e a versão 2b adicionando apenas o membro Endereço. Passar de 2a para 2b envolveria remover Age e adicionar Address; ir na outra direção implicaria remover o endereço e adicionar a idade. A remoção de membros não é permitida por estas diretrizes.
Geralmente, você não deve criar novos subtipos de tipos de contrato de dados existentes em uma nova versão do seu aplicativo. Da mesma forma, você não deve criar novos contratos de dados que são usados no lugar de membros de dados declarados como Objeto ou como tipos de interface. A criação dessas novas classes só é permitida quando você sabe que pode adicionar os novos tipos à lista de tipos conhecidos de todas as instâncias do seu aplicativo antigo. Por exemplo, na versão 1 do seu aplicativo, você pode ter o tipo de contrato de dados LibraryItem com os subtipos de contrato de dados Livro e Jornal. LibraryItem teria então uma lista de tipos conhecidos que contém Livro e Jornal. Suponha que agora você adicione um tipo de revista na versão 2, que é um subtipo de LibraryItem. Se você enviar uma instância do Magazine da versão 2 para a versão 1, o contrato de dados do Magazine não será encontrado na lista de tipos conhecidos e uma exceção será lançada.
Você não deve adicionar ou remover membros de enumeração entre versões. Você também não deve renomear membros de enumeração, a
EnumMemberAttribute
menos que use a propriedade Name no atributo para manter seus nomes no modelo de contrato de dados iguais.As coleções são intercambiáveis no modelo de contrato de dados, conforme descrito em Tipos de coleta em contratos de dados. Isto permite um grande grau de flexibilidade. No entanto, certifique-se de não alterar inadvertidamente um tipo de coleção de forma não intercambiável de versão para versão. Por exemplo, não mude de uma coleção não personalizada (ou seja, sem o
CollectionDataContractAttribute
atributo) para uma coleção personalizada ou uma coleção personalizada para uma coleção não personalizada. Além disso, não altere as propriedades naCollectionDataContractAttribute
versão de para versão. A única alteração permitida é adicionar uma propriedade Name ou Namespace se o nome ou namespace do tipo de coleção subjacente tiver sido alterado e você precisar tornar o nome do contrato de dados e o namespace iguais aos de uma versão anterior.
Algumas das diretrizes listadas aqui podem ser ignoradas com segurança quando circunstâncias especiais se aplicam. Certifique-se de entender completamente os mecanismos de serialização, desserialização e esquema envolvidos antes de se desviar das diretrizes.
Consulte também
- Name
- DataContractAttribute
- Order
- IsRequired
- IExtensibleDataObject
- ServiceBehaviorAttribute
- ExtensionData
- ExtensionDataObject
- OnDeserializingAttribute
- Usando contratos de dados
- Controle de versão de contrato de dados
- Nomes de contratos de dados
- Contratos de dados compatíveis com o futuro
- Retornos de chamada de serialização tolerantes à versão