Versiones de contratos de datosData Contract Versioning

A medida que las aplicaciones evolucionan, es posible que tenga que cambiar los contratos de datos que utilizan los servicios.As applications evolve, you may also have to change the data contracts the services use. En este tema se explica cómo controlar las versiones de los contratos de datos.This topic explains how to version data contracts. En este tema se describen los mecanismos de control de versiones de los contratos de datos.This topic describes the data contract versioning mechanisms. Para obtener información general completa y una guía de versiones prescriptiva , consulte prácticas recomendadas: Control de versionesdel contrato de datos.For a complete overview and prescriptive versioning guidance, see Best Practices: Data Contract Versioning.

Cambios con interrupción y Cambios sin interrupciónBreaking vs. Nonbreaking Changes

Los cambios en un contrato de datos pueden ser con o sin interrupción.Changes to a data contract can be breaking or nonbreaking. Cuando un contrato de datos se cambia de una manera sin interrupción, una aplicación que use la versión anterior del contrato puede comunicarse con una aplicación utilizando la versión más reciente y una aplicación que utilice la versión más reciente del contrato puede comunicarse con una aplicación que utilice la versión anterior.When a data contract is changed in a nonbreaking way, an application using the older version of the contract can communicate with an application using the newer version, and an application using the newer version of the contract can communicate with an application using the older version. Por otro lado, un cambio con interrupción evita la comunicación en una o ambas direcciones.On the other hand, a breaking change prevents communication in one or both directions.

Los cambios realizados en un tipo que no afecten a la forma de la transmisión y recepción son cambios sin interrupción.Any changes to a type that do not affect how it is transmitted and received are nonbreaking. Tales cambios no cambian el contrato de datos, solo el tipo subyacente.Such changes do not change the data contract, only the underlying type. Por ejemplo, puede cambiar el nombre de un campo de una manera sin interrupción si establece a continuación la propiedad Name de DataMemberAttribute en el nombre de la versión anterior.For example, you can change the name of a field in a nonbreaking way if you then set the Name property of the DataMemberAttribute to the older version name. El código siguiente muestra la versión 1 de un contrato de datos.The following code shows version 1 of a data contract.

// Version 1
[DataContract]
public class Person
{
    [DataMember]
    private string Phone;
}
' Version 1
<DataContract()>  _
Public Class Person
    <DataMember()>  _
    Private Phone As String
End Class 

El código siguiente muestra un cambio sin interrupción.The following code shows a nonbreaking change.

// Version 2. This is a non-breaking change because the data contract 
// has not changed, even though the type has.
[DataContract]
public class Person
{
    [DataMember(Name = "Phone")]
    private string Telephone;
}
' Version 2. This is a non-breaking change because the data contract 
' has not changed, even though the type has.
<DataContract()>  _
Public Class Person
    <DataMember(Name := "Phone")>  _
    Private Telephone As String
End Class 

Algunos cambios modifican los datos transmitidos, pero puede que sean o no cambios con interrupción.Some changes do modify the transmitted data, but may or may not be breaking. Los siguientes cambios siempre son con interrupción:The following changes are always breaking:

  • Cambio del valor Name o Namespace de un contrato de datos.Changing the Name or Namespace value of a data contract.

  • Cambio del orden de miembros de datos utilizando la propiedad Order de DataMemberAttribute.Changing the order of data members by using the Order property of the DataMemberAttribute.

  • Cambio de nombre de un miembro de datos.Renaming a data member.

  • Cambio del contrato de datos de un miembro de datos.Changing the data contract of a data member. Por ejemplo, cambiar el tipo de miembro de datos de entero a cadena, o de un tipo con un contrato de datos denominado "Cliente" a un tipo con un contrato de datos denominado "Persona."For example, changing the type of data member from an integer to a string, or from a type with a data contract named "Customer" to a type with a data contract named "Person".

Los cambios siguientes también son posibles.The following changes are also possible.

Agregar y eliminar miembros de datosAdding and Removing Data Members

En la mayoría de los casos, agregar o eliminar un miembro de datos no es un cambio con interrupción, a menos que requiera validez estricta de esquema (las nuevas instancias se validan frente al esquema anterior).In most cases, adding or removing a data member is not a breaking change, unless you require strict schema validity (new instances validating against the old schema).

Cuando un tipo con un campo adicional se deserializa en un tipo con un campo que falta, se pasa por alto la información adicional.When a type with an extra field is deserialized into a type with a missing field, the extra information is ignored. (También puede almacenarse para fines de ida y vuelta; para obtener más información, consulte contratos de datos compatibles con el avance).(It may also be stored for round-tripping purposes; for more information, see Forward-Compatible Data Contracts).

Cuando un tipo con un campo que falta se deserializa en un tipo con un campo adicional, el campo adicional se deja en su valor predeterminado, normalmente cero o null.When a type with a missing field is deserialized into a type with an extra field, the extra field is left at its default value, usually zero or null. (Se puede cambiar el valor predeterminado; para obtener más información, vea devoluciones de llamada de serialización tolerante a versiones).(The default value may be changed; for more information, see Version-Tolerant Serialization Callbacks.)

Por ejemplo, puede utilizar la clase CarV1 en un cliente y la clase CarV2 en un servicio, o puede utilizar la clase CarV1 en un servicio y la clase CarV2 en un cliente.For example, you can use the CarV1 class on a client and the CarV2 class on a service, or you can use the CarV1 class on a service and the CarV2 class on a client.

// Version 1 of a data contract, on machine V1.
[DataContract(Name = "Car")]
public class CarV1
{
    [DataMember]
    private string Model;
}

// Version 2 of the same data contract, on machine V2.
[DataContract(Name = "Car")]
public class CarV2
{
    [DataMember]
    private string Model;

    [DataMember]
    private int HorsePower;
}
' Version 1 of a data contract, on machine V1.
<DataContract(Name := "Car")>  _
Public Class CarV1
    <DataMember()>  _
    Private Model As String
End Class 

' Version 2 of the same data contract, on machine V2.
<DataContract(Name := "Car")>  _
Public Class CarV2
    <DataMember()>  _
    Private Model As String
    
    <DataMember()>  _
    Private HorsePower As Integer
End Class 

El punto de conexión de versión 2 puede enviar datos correctamente al punto de conexión de versión 1.The version 2 endpoint can successfully send data to the version 1 endpoint. La serialización de la versión 2 del contrato de datos de Car produce un XML similar al siguiente.Serializing version 2 of the Car data contract yields XML similar to the following.

<Car>  
    <Model>Porsche</Model>  
    <HorsePower>300</HorsePower>  
</Car>  

El motor de deserialización en V1 no encuentra un miembro de datos coincidente para el campo HorsePower y descarta esos datos.The deserialization engine on V1 does not find a matching data member for the HorsePower field, and discards that data.

Asimismo, el punto de conexión de versión 1 puede enviar datos al punto de conexión de versión 2.Also, the version 1 endpoint can send data to the version 2 endpoint. La serialización de la versión 1 del contrato de datos de Car produce un XML similar al siguiente.Serializing version 1 of the Car data contract yields XML similar to the following.

<Car>  
    <Model>Porsche</Model>  
</Car>  

El deserializador de versión 2 no sabe qué valor asignar al campo HorsePower, porque no hay datos coincidentes en el XML de entrada.The version 2 deserializer does not know what to set the HorsePower field to, because there is no matching data in the incoming XML. En su lugar, el campo se establece en el valor predeterminado de 0.Instead, the field is set to the default value of 0.

Miembros de datos necesariosRequired Data Members

Un miembro de datos se puede marcar como necesario estableciendo la propiedad IsRequired de DataMemberAttribute en true.A data member may be marked as being required by setting the IsRequired property of the DataMemberAttribute to true. Si faltan datos necesarios al deserializar, se genera una excepción en lugar de establecer el miembro de datos en su valor predeterminado.If required data is missing while deserializing, an exception is thrown instead of setting the data member to its default value.

Agregar un miembro de datos necesario es un cambio con interrupción.Adding a required data member is a breaking change. Es decir, el tipo más nuevo todavía puede enviarse a los puntos de conexión con el tipo anterior, pero no al revés.That is, the newer type can still be sent to endpoints with the older type, but not the other way around. Eliminar un miembro de datos marcado como necesario en cualquier versión anterior también es un cambio con interrupción.Removing a data member that was marked as required in any prior version is also a breaking change.

Cambiar el valor de la propiedad IsRequired de true a false no es un cambio con interrupción, pero cambiarlo de false a true puede ser un cambio con interrupción si cualquier versión anterior del tipo no tiene el miembro de datos en cuestión.Changing the IsRequired property value from true to false is not breaking, but changing it from false to true may be breaking if any prior versions of the type do not have the data member in question.

Nota

Aunque la propiedad IsRequired se establezca en true, el dato entrante puede ser nulo o cero, y se debe preparar un tipo para afrontar esta posibilidad.Although the IsRequired property is set to true, the incoming data may be null or zero, and a type must be prepared to handle this possibility. No utilice IsRequired como mecanismo de seguridad frente a datos entrante no válidos.Do not use IsRequired as a security mechanism to protect against bad incoming data.

Valores predeterminados ignoradosOmitted Default Values

Es posible (aunque no se recomienda) establecer la EmitDefaultValue propiedad en el atributo DataMemberAttribute en false, tal y como se describe en valores predeterminados de los miembros de datos.It is possible (although not recommended) to set the EmitDefaultValue property on the DataMemberAttribute attribute to false, as described in Data Member Default Values. Si este valor es false, no se emitirá el miembro de datos si está establecido en su valor predeterminado (normalmente null o cero).If this setting is false, the data member will not be emitted if it is set to its default value (usually null or zero). Esto no es compatible con miembros de datos necesarios en versiones diferentes de dos maneras:This is not compatible with required data members in different versions in two ways:

  • Un contrato de datos con un miembro de datos necesario en una versión no puede recibir datos predeterminados (null o cero) de una versión diferente en la que el miembro de datos tenga EmitDefaultValue establecido en false.A data contract with a data member that is required in one version cannot receive default (null or zero) data from a different version in which the data member has EmitDefaultValue set to false.

  • Un miembro de datos necesario que tiene EmitDefaultValue establecido en false no se puede utilizar para serializar su valor predeterminado (null o cero), pero puede recibir este tipo de valor en la deserialización.A required data member that has EmitDefaultValue set to false cannot be used to serialize its default (null or zero) value, but can receive such a value on deserialization. Esto crea un problema de ida y vuelta (los datos se pueden leer pero los mismos datos no se pueden escribir a continuación).This creates a round-tripping problem (data can be read in but the same data cannot then be written out). Por consiguiente, si IsRequired es true y EmitDefaultValue es false en una versión, la misma combinación se debería aplicar al resto de versiones, de tal manera que ninguna versión del contrato de datos pudiese generar un valor que no resulte en un recorrido de ida y vuelta.Therefore, if IsRequired is true and EmitDefaultValue is false in one version, the same combination should apply to all other versions such that no version of the data contract would be able to produce a value that does not result in a round trip.

Consideraciones del esquemaSchema Considerations

Para obtener una explicación del esquema que se genera para los tipos de contrato de datos, vea referencia de esquema de contrato de datos.For an explanation of what schema is produced for data contract types, see Data Contract Schema Reference.

El esquema que WCF genera para los tipos de contrato de datos no hace ninguna disposición para el control de versiones.The schema WCF produces for data contract types makes no provisions for versioning. Es decir, el esquema exportado desde una cierta versión de un tipo contiene solo esos miembros de datos presentes en esa versión.That is, the schema exported from a certain version of a type contains only those data members present in that version. La implementación de la interfaz IExtensibleDataObject, no cambia el esquema de un tipo.Implementing the IExtensibleDataObject interface does not change the schema for a type.

Los miembros de datos se exportan de forma predeterminada al esquema como elementos opcionales.Data members are exported to the schema as optional elements by default. Es decir, el valor de minOccurs (atributo XML) se establece en 0.That is, the minOccurs (XML attribute) value is set to 0. Los miembros de datos necesarios se exportan con minOccurs establecido en 1.Required data members are exported with minOccurs set to 1.

Muchos de los cambios considerados como cambios sin interrupción son, en realidad, cambios con interrupción si se requiere una adherencia estricta al esquema.Many of the changes considered to be nonbreaking are actually breaking if strict adherence to the schema is required. En el ejemplo anterior, una instancia de CarV1 simplemente con el elemento Model validaría frente al esquema CarV2 (que tiene Model y Horsepower, pero ambos son opcionales).In the preceding example, a CarV1 instance with just the Model element would validate against the CarV2 schema (which has both Model and Horsepower, but both are optional). Sin embargo, lo inverso no es cierto: una instancia de CarV2 produciría un error en la validación frente al esquema CarV1.However, the reverse is not true: a CarV2 instance would fail validation against the CarV1 schema.

El recorrido de ida y vuelta implica algunas consideraciones adicionales.Round-tripping also entails some additional considerations. Para obtener más información, vea la sección "consideraciones del esquema" en contratos de datos compatibles con versiones posteriores.For more information, see the "Schema Considerations" section in Forward-Compatible Data Contracts.

Otros cambios permitidosOther Permitted Changes

Implementar la interfaz IExtensibleDataObject es un cambio sin interrupción.Implementing the IExtensibleDataObject interface is a nonbreaking change. Sin embargo, la compatibilidad con el recorrido de ida y vuelta no existe para las versiones del tipo anterior a la versión en la que IExtensibleDataObject se implementó.However, round-tripping support does not exist for versions of the type prior to the version in which IExtensibleDataObject was implemented. Para obtener más información, vea Forward-Compatible Data Contracts (Contratos de datos compatibles con el reenvío).For more information, see Forward-Compatible Data Contracts.

EnumeracionesEnumerations

Agregar o eliminar un miembro de enumeración es un cambio con interrupción.Adding or removing an enumeration member is a breaking change. Cambiar el nombre de un miembro de enumeración es un cambio con interrupción, a menos que su nombre de contrato se mantenga igual que en la versión anterior mediante el atributo EnumMemberAttribute.Changing the name of an enumeration member is breaking, unless its contract name is kept the same as in the old version by using the EnumMemberAttribute attribute. Para obtener más información, vea tipos de enumeración en contratos de datos.For more information, see Enumeration Types in Data Contracts.

ColeccionesCollections

La mayoría de los cambios de colección son cambios sin interrupción, puesto que la mayoría de los tipos de colección son intercambiables entre sí en el modelo del contrato de datos.Most collection changes are nonbreaking because most collection types are interchangeable with each other in the data contract model. Sin embargo, personalizar una colección no personalizada o viceversa es un cambio con interrupción.However, making a noncustomized collection customized or vice versa is a breaking change. Asimismo, cambiar la configuración de personalización de la colección es un cambio brusco; es decir, implica cambiar su espacio de nombres y nombre de contrato de datos, repitiendo el nombre del elemento, el nombre del elemento de la clave y el nombre del elemento del valor.Also, changing the collection's customization settings is a breaking change; that is, changing its data contract name and namespace, repeating element name, key element name, and value element name. Para obtener más información sobre la personalización de colecciones, vea tipos de colección en contratos de datos.For more information about collection customization, see Collection Types in Data Contracts.
Naturalmente, cambiar el contrato de datos del contenido de una colección (por ejemplo, cambiar de una lista de enteros a una lista de cadenas) es un cambio brusco.Naturally, changing the data contract of contents of a collection (for example, changing from a list of integers to a list of strings) is a breaking change.

Vea tambiénSee also