Compartir a través de


Control de versiones del servicio

Después de la implementación inicial, y potencialmente varias veces durante su duración, es posible que los servicios (y los puntos de conexión que exponen) deban cambiarse por diversos motivos, como cambiar las necesidades empresariales, los requisitos de tecnología de la información o solucionar otros problemas. Cada cambio presenta una nueva versión del servicio. En este tema se explica cómo considerar el control de versiones en Windows Communication Foundation (WCF).

Cuatro categorías de cambios de servicio

Los cambios en los servicios que pueden ser necesarios se pueden clasificar en cuatro categorías:

  • Cambios de contrato: por ejemplo, se puede agregar una operación o se podría agregar o cambiar un elemento de datos en un mensaje.

  • Cambios de dirección: por ejemplo, un servicio se mueve a una ubicación diferente donde los puntos de conexión tienen nuevas direcciones.

  • Cambios del enlace: por ejemplo, un mecanismo de seguridad cambia o lo hace su configuración.

  • Cambios de implementación: por ejemplo, cuando cambia una implementación de método interna.

Algunos de estos cambios se denominan "importantes" y otros son "no disruptivos". Un cambio es no disruptivo si todos los mensajes que se habrían procesado correctamente en la versión anterior se procesan correctamente en la nueva versión. Cualquier cambio que no cumpla ese criterio es un cambio importante .

Orientación del servicio y control de versiones

Uno de los principios de orientación de servicio es que los servicios y clientes son autónomos (o independientes). Entre otras cosas, esto implica que los desarrolladores de servicios no pueden asumir que controlan o incluso conocen todos los clientes de servicio. Esto elimina la opción de volver a generar y volver a implementar todos los clientes cuando un servicio cambia las versiones. En este tema se supone que el servicio se adhiere a este principio y, por tanto, debe cambiar o "versionarse" independientemente de sus clientes.

En los casos en los que no se prevé un cambio con interrupción y no puede evitarse, una aplicación puede decidir si omitir este principio y requerir que los clientes se recompilen y se implementen de nuevo con una nueva versión del servicio.

Versionado de contratos

Los contratos utilizados por un cliente no necesitan ser los mismos que el contrato utilizado por el servicio; solo deben ser compatibles.

En el caso de los contratos de servicio, la compatibilidad significa que se pueden agregar nuevas operaciones expuestas por el servicio, pero las operaciones existentes no se pueden quitar ni cambiar semánticamente.

En el caso de los contratos de datos, la compatibilidad significa que se pueden agregar nuevas definiciones de tipo de esquema, pero las definiciones de tipo de esquema existentes no se pueden cambiar de maneras importantes. Los cambios con interrupción podrían incluir quitar los miembros de datos o cambiar de manera incompatible su tipo de datos. Esta función ofrece al servicio cierta flexibilidad para actualizar la versión de sus contratos sin causar problemas a los clientes. Las dos secciones siguientes explican qué cambios no disruptivos e importantes pueden realizarse en los contratos de datos y servicio de WCF.

Control de versiones del contrato de datos

En esta sección se trata el control de versiones de datos al usar las DataContractSerializer clases y DataContractAttribute .

Control estricto de versiones

En muchos escenarios cuando cambiar las versiones es un problema, el desarrollador del servicio no tiene control sobre los clientes y, por tanto, no puede hacer suposiciones sobre cómo reaccionarían a los cambios en el xml del mensaje o el esquema. En estos casos, debe garantizar que los nuevos mensajes se validarán con el esquema anterior, por dos razones:

  • Los clientes antiguos se desarrollaron con la suposición de que el esquema no cambiará. Es posible que no procesen mensajes para los que nunca se diseñaron.

  • Los clientes anteriores pueden realizar la validación del esquema real en el esquema anterior antes de intentar, incluso, procesar los mensajes.

El enfoque recomendado en estos escenarios es tratar los contratos de datos existentes como inmutables y crear nuevos con nombres completos XML únicos. Después, el desarrollador de servicios agregaría nuevos métodos a un contrato de servicio existente o crearía un nuevo contrato de servicio con métodos que usan el nuevo contrato de datos.

A menudo, será el caso de que un desarrollador de servicios necesite escribir alguna lógica de negocios que se ejecute dentro de todas las versiones de un contrato de datos más el código de negocio específico de la versión para cada versión del contrato de datos. El apéndice al final de este tema explica cómo se pueden usar las interfaces para satisfacer esta necesidad.

Control de versiones lax

En muchos otros escenarios, el desarrollador de servicios puede suponer que agregar un nuevo miembro opcional al contrato de datos no interrumpirá los clientes existentes. Esto requiere que el desarrollador de servicios investigue si los clientes existentes no realizan la validación de esquemas y ignoran los miembros de datos desconocidos. En estos escenarios, es posible aprovecharse de las características del contrato de datos para agregar los nuevos miembros sin interrupción. El desarrollador del servicio puede hacer esta suposición con confianza si las características del contrato de datos para el control de versiones ya se usaron para la primera versión del servicio.

WCF, servicios web de ASP.NET y muchas otras pilas de servicios web admiten el versionado flexible: es decir, no lanzan excepciones para miembros de datos nuevos y desconocidos en los datos recibidos.

Es fácil creer erróneamente que agregar un nuevo miembro no interrumpirá los clientes existentes. Si no está seguro de que todos los clientes puedan manejar el versionado flexible, la recomendación es usar las directrices de versionado estrictas y tratar los contratos de datos como inmutables.

Para obtener instrucciones detalladas para el control de versiones lax y estricta de los contratos de datos, consulte Procedimientos recomendados: Control de versiones del contrato de datos.

Distinguir entre los tipos de contrato de datos y .NET

Una clase o estructura de .NET se puede proyectar como un contrato de datos aplicando el DataContractAttribute atributo a la clase . El tipo de .NET y sus proyecciones de contrato de datos son dos aspectos distintos. Es posible tener varios tipos de .NET con la misma proyección de contrato de datos. Esta distinción es especialmente útil para permitirle cambiar el tipo de .NET mientras mantiene el contrato de datos proyectado, manteniendo así la compatibilidad con los clientes existentes incluso en el sentido estricto de la palabra. Hay dos cosas que debe hacer siempre para mantener esta distinción entre el tipo de .NET y el contrato de datos:

  • Especifique Name y Namespace. Siempre debería especificar el nombre y el espacio de nombres de su contrato de datos para evitar que el nombre y el espacio de nombres del tipo .NET se expongan en el contrato. De este modo, si decide más adelante cambiar el espacio de nombres o el nombre de tipo de .NET, el contrato de datos sigue siendo el mismo.

  • Especifique Name. Siempre debes especificar el nombre de los miembros de tus datos para evitar que el nombre de tus miembros de .NET se exponga en el contrato. De este modo, si decide más adelante cambiar el nombre de .NET del miembro, el contrato de datos sigue siendo el mismo.

Cambiar o quitar miembros

Cambiar el nombre o el tipo de datos de un miembro, o quitar los miembros de datos se considera un cambio con interrupción, aun cuando se permite un control poco estricto de las versiones. Si es necesario, cree un nuevo contrato de datos.

Si la compatibilidad del servicio es de importancia elevada, podría considerar omitir los miembros de datos no usados en su código y dejarlos en su lugar. Si se divide un miembro de datos en varios miembros, se podría optar por dejar el miembro existente como una propiedad que pueda realizar la división necesaria y volver a agregar los clientes de nivel inferior (clientes que no se han actualizado a la última versión).

De igual forma, los cambios realizados en el espacio de nombres o el nombre del contrato de datos son cambios importantes.

Ida y vuelta de datos desconocidos

En algunos escenarios, hay una necesidad de realizar una "ida y vuelta" de datos desconocidos que proceden de los miembros agregados en una nueva versión. Por ejemplo, un servicio "versionNew" envía datos con algunos miembros recién agregados a un cliente "versionOld". El cliente omite los miembros recién agregados al procesar el mensaje, pero vuelve a enviar esos mismos datos, incluidos los miembros recién agregados, a la versiónNuevo servicio. El escenario típico de esto es las actualizaciones de datos en las que los datos se recuperan del servicio, se cambian y devuelven.

Para habilitar la ida y vuelta para un tipo determinado, el tipo debe implementar la IExtensibleDataObject interfaz. La interfaz contiene una propiedad , ExtensionData que devuelve el ExtensionDataObject tipo . La propiedad se utiliza para almacenar cualquier dato de las versiones futuras del contrato de datos que es desconocido para la versión actual. Estos datos son opacos para el cliente, pero cuando se serializa la instancia, el contenido de la ExtensionData propiedad se escribe con el resto de los datos de los miembros del contrato de datos.

Se recomienda que todos los tipos implementen esta interfaz para dar cabida a miembros futuros nuevos y desconocidos.

Bibliotecas de contratos de datos

Puede haber bibliotecas de contratos de datos en los que un contrato se publica en un repositorio central, y los implementadores de servicios y tipos implementan y exponen contratos de datos desde ese repositorio. En ese caso, al publicar un contrato de datos en el repositorio, no tiene control sobre quién crea tipos que lo implementan. Por lo tanto, no se puede modificar el contrato una vez publicado, haciéndolo efectivamente inmutable.

Al usar el XmlSerializer

Se aplican los mismos principios de control de versiones al usar la XmlSerializer clase . Cuando se requiere el control de versiones estrictos, trate los contratos de datos como inmutables y cree nuevos contratos de datos con nombres únicos y calificados para las nuevas versiones. Cuando esté seguro de que se puede utilizar un versionado flexible, puede agregar nuevos miembros serializables a versiones nuevas, pero no cambiar ni quitar miembros existentes.

Nota:

XmlSerializer utiliza XmlAnyElementAttribute y los atributos XmlAnyAttributeAttribute para admitir viajes de ida y vuelta de los datos desconocidos.

Versiones de contratos de mensaje

Las guías para el versionado de contratos de mensaje son muy similares al versionado de contratos de datos. Si se requiere el control de versiones estricto, no debe cambiar el contenido del mensaje, sino crear un nuevo contrato de mensaje con un nombre calificado único. Si sabe que puede usar un versionado flexible, puede agregar nuevas partes al cuerpo del mensaje, pero no cambiar ni quitar las existentes. Esta orientación se aplica tanto a los contratos de mensaje sencillos como a los ajustados.

Los encabezados de mensaje siempre se pueden agregar, incluso si el control de versiones estricto está en uso. La marca MustUnderstand puede afectar a las versiones. En general, el modelo de control de versiones para los encabezados de WCF es como se describe en la especificación SOAP.

Control de versiones del contrato de servicio

De forma similar al control de versiones del contrato de datos, el control de versiones del contrato de servicio también implica agregar, cambiar y quitar operaciones.

Especificar nombre, espacio de nombres y acción

De forma predeterminada, el nombre de un contrato de servicio es el nombre de la interfaz. Su espacio de nombres predeterminado es http://tempuri.orgy la acción de cada operación es http://tempuri.org/contractname/methodname. Se recomienda especificar explícitamente un nombre y un espacio de nombres para el contrato de servicio, y una acción para cada operación para evitar el uso http://tempuri.org y evitar que los nombres de interfaz y método se expongan en el contrato del servicio.

Adición de parámetros y operaciones

Agregar operaciones del servicio expuestas por el servicio es un cambio sin interrupción porque los clientes existentes no necesitan ocuparse de esas nuevas operaciones.

Nota:

Agregar las operaciones a un contrato de devolución de llamada dúplex es un cambio con interrupción.

Cambio de parámetro de operación o tipos de valor devuelto

El cambio de parámetros o tipos de valor devuelto suele ser un cambio importante a menos que el nuevo tipo implemente el mismo contrato de datos implementado por el tipo anterior. Para realizar este cambio, agregue una nueva operación al contrato de servicio o defina un nuevo contrato de servicio.

Quitar operaciones

Quitar las operaciones también es un cambio con interrupción. Para realizar este cambio, defina un nuevo contrato de servicio y expóselo en un nuevo punto de conexión.

Contrato de error

El FaultContractAttribute atributo permite que un desarrollador de contratos de servicio especifique información sobre los errores que se pueden devolver de las operaciones del contrato.

La lista de errores descritos en el contrato de un servicio no se considera exhaustiva. En cualquier momento, una operación puede devolver errores que no se describen en su contrato. Por lo tanto, cambiar el conjunto de fallos descritos en el contrato no se considera una ruptura. Por ejemplo, agregar un nuevo error al contrato mediante FaultContractAttribute o quitar un error existente del contrato.

Bibliotecas de contratos de servicio

Las organizaciones pueden tener bibliotecas de contratos en los que un contrato se publica en un repositorio central y los implementadores de servicios implementan contratos de ese repositorio. En este caso, al publicar un contrato de servicio en el repositorio, no tiene control sobre quién crea servicios que lo implementan. Por lo tanto, no se puede modificar el contrato de servicio una vez publicado, lo que lo hace prácticamente inmutable. WCF admite la herencia de contratos, que se puede usar para crear un nuevo contrato que extienda los contratos existentes. Para usar esta característica, defina una nueva interfaz de contrato de servicio que herede de la interfaz anterior del contrato de servicio y agregue métodos a la nueva interfaz. A continuación, cambie el servicio que implementa el contrato anterior para implementar el nuevo contrato y cambie la definición de punto de conexión "versionOld" para usar el nuevo contrato. Para los clientes con la "versionOld", el punto de conexión seguirá mostrándose como si se estuviera exponiendo el contrato "versionOld". En cambio, para los clientes con la "versionNew", el punto de conexión aparecerá exponiendo el contrato "versionNew".

Dirección y enlace de las versiones

Los cambios en la dirección del punto de conexión y el enlace son cambios importantes a menos que los clientes puedan detectar dinámicamente la nueva dirección o enlace del punto de conexión. Un mecanismo para implementar esta funcionalidad es mediante un Registro de Descripción e Integración de Descubrimiento Universal (UDDI) y el Patrón de Invocación UDDI, donde un cliente intenta comunicarse con un punto de conexión y, tras un error, consulta un registro UDDI conocido para obtener los metadatos del punto de conexión actual. A continuación, el cliente usa la dirección y el enlace de estos metadatos para comunicarse con el punto de conexión. Si esta comunicación se realiza correctamente, el cliente almacena en caché la información de dirección y enlace para su uso futuro.

Servicio de enrutamiento y control de versiones

Si los cambios realizados en un servicio son cambios importantes y debe tener dos o más versiones diferentes de un servicio que se ejecutan simultáneamente, puede usar el servicio de enrutamiento de WCF para enrutar mensajes a la instancia de servicio adecuada. El servicio de enrutamiento de WCF usa el enrutamiento basado en contenido, es decir, usa información dentro del mensaje para determinar dónde enrutar el mensaje. Para obtener más información sobre el servicio de enrutamiento de WCF, vea Servicio de enrutamiento. Para obtener un ejemplo de cómo usar el servicio de enrutamiento de WCF para el control de versiones del servicio, vea How To: Service Versioning.

Apéndice

La guía de control de versiones del contrato de datos general cuando se necesita el control de versiones estricto es tratar los contratos de datos como inmutables y crear nuevos cuando se requieren cambios. Es necesario crear una nueva clase para cada nuevo contrato de datos, por lo que se necesita un mecanismo para evitar tener que tomar código existente escrito en términos de la clase de contrato de datos antigua y volver a escribirla en términos de la nueva clase de contrato de datos.

Un mecanismo de este tipo es usar interfaces para definir los miembros de cada contrato de datos y escribir código de implementación interno en términos de interfaces en lugar de las clases de contrato de datos que implementan las interfaces. El código siguiente para la versión 1 de un servicio muestra una IPurchaseOrderV1 interfaz y un PurchaseOrderV1:

public interface IPurchaseOrderV1  
{  
    string OrderId { get; set; }  
    string CustomerId { get; set; }  
}  
  
[DataContract(  
Name = "PurchaseOrder",  
Namespace = "http://examples.microsoft.com/WCF/2005/10/PurchaseOrder")]  
public class PurchaseOrderV1 : IPurchaseOrderV1  
{  
    [DataMember(...)]  
    public string OrderId {...}  
    [DataMember(...)]  
    public string CustomerId {...}  
}  

Aunque las operaciones del contrato de servicio se escribirían en términos de PurchaseOrderV1, la lógica de negocios real sería en términos de IPurchaseOrderV1. A continuación, en la versión 2, habría una nueva IPurchaseOrderV2 interfaz y una nueva PurchaseOrderV2 clase como se muestra en el código siguiente:

public interface IPurchaseOrderV2  
{  
    DateTime OrderDate { get; set; }  
}

[DataContract(
Name = "PurchaseOrder",  
Namespace = "http://examples.microsoft.com/WCF/2006/02/PurchaseOrder")]  
public class PurchaseOrderV2 : IPurchaseOrderV1, IPurchaseOrderV2  
{  
    [DataMember(...)]  
    public string OrderId {...}  
    [DataMember(...)]  
    public string CustomerId {...}  
    [DataMember(...)]  
    public DateTime OrderDate { ... }  
}  

El contrato de servicio se actualizaría para incluir nuevas operaciones escritas en términos de PurchaseOrderV2. La lógica de negocios existente escrita en términos de IPurchaseOrderV1 seguiría funcionando para PurchaseOrderV2, y la nueva lógica de negocios que necesita la propiedad OrderDate se escribiría en términos de IPurchaseOrderV2.

Consulte también