Compartir a través de


Versiones de contratos de datos

A medida que las aplicaciones evolucionan, es posible que tenga que cambiar los contratos de datos que utilizan los servicios. En este tema se explica cómo controlar las versiones de los contratos de datos. En este tema se describen los mecanismos de control de versiones de los contratos de datos. Para obtener una información general completa y una guía prescriptiva sobre el control de versiones, vea Procedimientos recomendados: Creación de versiones de contratos de datos.

Cambios con interrupción y sin interrupción

Los cambios en un contrato de datos pueden ser con o sin interrupción. 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. Por otro lado, un cambio con interrupción evita la comunicación en una o ambas direcciones.

Los cambios realizados en un tipo que no afecten a la forma de la transmisión y recepción son cambios sin interrupción. Tales cambios no cambian el contrato de datos, solo el tipo subyacente. 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. El código siguiente muestra la versión 1 de un contrato de datos.

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

El código siguiente muestra un cambio sin interrupción.

' 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 
// 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;
}

Algunos cambios modifican los datos transmitidos, pero puede que sean o no cambios con interrupción. Los siguientes cambios siempre son con interrupción:

  • Cambio del valor Name o Namespace de un contrato de datos.

  • Cambio del orden de miembros de datos utilizando la propiedad Order de DataMemberAttribute.

  • Cambio de nombre de un miembro de datos.

  • Cambio del contrato de datos de un miembro de datos. 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."

Los cambios siguientes también son posibles.

Agregar y eliminar miembros de datos

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).

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. (También puede almacenarse para recorridos de ida y vuelta; para más información, vea Contratos de datos compatibles con el reenvío).

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. (Se puede cambiar el valor predeterminado; para más información, vea Devoluciones de llamadas en la serialización tolerante a versiones.)

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.

' 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 
// 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;
}

El extremo de versión 2 puede enviar datos correctamente al extremo de versión 1. La serialización de la versión 2 del contrato de datos de Car produce un XML similar al siguiente.

<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.

Asimismo, el extremo de versión 1 puede enviar datos al extremo de versión 2. La serialización de la versión 1 del contrato de datos de Car produce un XML similar al siguiente.

<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. En su lugar, el campo se establece en el valor predeterminado de 0.

Miembros de datos necesarios

Un miembro de datos se puede marcar como necesario estableciendo la propiedad IsRequired de DataMemberAttribute en true. Si faltan datos necesarios al deserializar, se genera una excepción en lugar de establecer el miembro de datos en su valor predeterminado.

Agregar un miembro de datos necesario es un cambio con interrupción. Es decir, el tipo más nuevo todavía puede enviarse a los extremos con el tipo anterior, pero no al revés. Eliminar un miembro de datos marcado como necesario en cualquier versión anterior también es un cambio con interrupción.

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.

ms731138.note(es-es,VS.100).gifNota:
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. No utilice IsRequired como mecanismo de seguridad frente a datos entrante no válidos.

Valores predeterminados ignorados

Es posible (aunque no recomendable) establecer la propiedad EmitDefaultValue en el atributo DataMemberAttribute en false, tal y como se describe en Valores predeterminados de los miembros de datos. Si este valor es false, no se emitirá el miembro de datos si está establecido en su valor predeterminado (normalmente null o cero). Esto no es compatible con miembros de datos necesarios en versiones diferentes de dos maneras:

  • 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.

  • 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. Esto crea un problema de ida y vuelta (los datos se pueden leer pero los mismos datos no se pueden escribir a continuación). 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.

Consideraciones del esquema

Para obtener una explicación sobre qué esquema se genera para los tipos de contrato de datos, vea Referencia de esquema de contrato de datos.

El esquema de WCF generado para los tipos de contrato de datos no realiza ninguna previsión para el control de versiones. Es decir, el esquema exportado desde una cierta versión de un tipo contiene solo esos miembros de datos presentes en esa versión. La implementación de la interfaz IExtensibleDataObject, no cambia el esquema de un tipo.

Los miembros de datos se exportan de forma predeterminada al esquema como elementos opcionales. Es decir, el valor de minOccurs (atributo XML) se establece en 0. Los miembros de datos necesarios se exportan con minOccurs establecido en 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. 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). Sin embargo, lo inverso no es cierto: una instancia de CarV2 produciría un error en la validación frente al esquema CarV1.

El recorrido de ida y vuelta implica algunas consideraciones adicionales. Para obtener más información, vea la sección “Consideraciones del esquema” en Contratos de datos compatibles con el reenvío.

Otros cambios permitidos

Implementar la interfaz IExtensibleDataObject es un cambio sin interrupción. 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ó. Para obtener más información, vea Contratos de datos compatibles con el reenvío.

Enumeraciones

Agregar o eliminar un miembro de enumeración es un cambio con interrupción. 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 EnumMemberAtttribute. Para obtener más información, vea Tipos de enumeración en contratos de datos.

Colecciones

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. Sin embargo, personalizar una colección no personalizada o viceversa es un cambio con interrupción. 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, repetir el nombre del elemento, el nombre del elemento de la clave y el nombre del elemento del valor. Para obtener más información sobre la personalización de la colección, vea Tipos de colección en contratos de datos.

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.

Vea también

Referencia

Name
DataMemberAttribute
Name
Namespace
Order
IsRequired
SerializationException
IExtensibleDataObject

Conceptos

Devoluciones de llamadas en la serialización tolerante a versiones
Procedimientos recomendados: Creación de versiones de contratos de datos
Utilización de contratos de datos
Equivalencia del contrato de datos
Contratos de datos compatibles con el reenvío