Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este tema se describe qué son los contratos de servicio, cómo se definen, qué operaciones están disponibles (y las implicaciones para los intercambios de mensajes subyacentes), qué tipos de datos se usan y otros problemas que le ayudan a diseñar operaciones que cumplan los requisitos de su escenario.
Creación de un contrato de servicio
Los servicios exponen varias operaciones. En las aplicaciones de Windows Communication Foundation (WCF), defina las operaciones mediante la creación de un método y su marcado con el OperationContractAttribute atributo . A continuación, para crear un contrato de servicio, agrupe las operaciones, ya sea declarandolas dentro de una interfaz marcada con el ServiceContractAttribute atributo o definiéndolas en una clase marcada con el mismo atributo. (Para obtener un ejemplo básico, vea Cómo: Definir un contrato de servicio).
Los métodos que no tienen un atributo OperationContractAttribute no son operaciones de servicio y no son expuestos por los servicios WCF.
En este tema se describen los siguientes puntos de decisión al diseñar un contrato de servicio:
Si se van a usar clases o interfaces.
Cómo especificar los tipos de datos que desea intercambiar.
Los tipos de patrones de intercambio que puede usar.
Si puede hacer que los requisitos de seguridad explícitos formen parte del contrato.
Restricciones para las entradas y salidas de operación.
Clases o interfaces
Ambas clases e interfaces representan una agrupación de funcionalidades y, por lo tanto, ambas se pueden usar para definir un contrato de servicio WCF. Sin embargo, se recomienda usar interfaces porque modelan directamente contratos de servicio. Sin una implementación, las interfaces no más que definen una agrupación de métodos con determinadas firmas. Implemente una interfaz de contrato de servicio y haya implementado un servicio WCF.
Todas las ventajas de las interfaces administradas se aplican a las interfaces de contrato de servicio:
Las interfaces de contrato de servicio pueden ampliar cualquier número de otras interfaces de contrato de servicio.
Una sola clase puede implementar cualquier número de contratos de servicio mediante la implementación de esas interfaces de contrato de servicio.
Puede modificar la implementación de un contrato de servicio cambiando la implementación de la interfaz, mientras que el contrato de servicio sigue siendo el mismo.
Puede versionar el servicio implementando la interfaz antigua y la nueva. Los clientes antiguos se conectan a la versión original, mientras que los clientes más recientes pueden conectarse a la versión más reciente.
Nota:
Al heredar de otras interfaces de contrato de servicio, no se pueden invalidar las propiedades de la operación, como el nombre o el espacio de nombres. Si intenta hacerlo, cree una nueva operación en el contrato de servicio actual.
Para obtener un ejemplo del uso de una interfaz para crear un contrato de servicio, vea How to: Create a Service with a Contract Interface.
Sin embargo, puede usar una clase para definir un contrato de servicio e implementar ese contrato al mismo tiempo. La ventaja de crear los servicios aplicando ServiceContractAttribute y OperationContractAttribute directamente a la clase y los métodos de la clase, respectivamente, es la velocidad y la simplicidad. Las desventajas son que las clases administradas no admiten varias herencias y, como resultado, solo pueden implementar un contrato de servicio a la vez. Además, cualquier modificación de las firmas de clase o método modifica el contrato público para ese servicio, lo que puede impedir que los clientes no modificados usen el servicio. Para obtener más información, consulte Implementación de contratos de servicio.
Para obtener un ejemplo que usa una clase para crear un contrato de servicio e implementarlo al mismo tiempo, vea How to: Create a Service with a Contract Class.
En este momento, debe comprender la diferencia entre definir el contrato de servicio mediante una interfaz y mediante una clase . El siguiente paso consiste en decidir qué datos se pueden pasar entre un servicio y sus clientes.
Parámetros y valores devueltos
Cada operación tiene un valor devuelto y un parámetro, incluso si son void
. Sin embargo, a diferencia de un método local, en el que puede pasar referencias a objetos de un objeto a otro, las operaciones de servicio no pasan referencias a objetos. En su lugar, pasan copias de los objetos.
Esto es significativo porque cada tipo utilizado en un parámetro o valor devuelto debe ser serializable; es decir, debe ser posible convertir un objeto de ese tipo en una secuencia de bytes y de una secuencia de bytes en un objeto .
Los tipos primitivos son serializables de forma predeterminada, ya que son muchos tipos en .NET Framework.
Nota:
El valor de los nombres de parámetro en la firma de la operación forma parte del contrato y distingue entre mayúsculas y minúsculas. Si desea usar el mismo nombre de parámetro localmente, pero modificar el nombre en los metadatos publicados, vea el System.ServiceModel.MessageParameterAttribute.
Contratos de datos
Las aplicaciones orientadas a servicios, como las aplicaciones de Windows Communication Foundation (WCF) están diseñadas para interoperar con el mayor número posible de aplicaciones cliente en plataformas de Microsoft y que no son de Microsoft. Para obtener la interoperabilidad más amplia posible, se recomienda marcar los tipos con los DataContractAttribute atributos y DataMemberAttribute para crear un contrato de datos, que es la parte del contrato de servicio que describe los datos que intercambian las operaciones de servicio.
Los contratos de datos son contratos de estilo de participación: ningún tipo o miembro de datos se serializa a menos que aplique explícitamente el atributo de contrato de datos. Los contratos de datos no están relacionados con el ámbito de acceso del código administrado: se pueden serializar y enviar miembros de datos privados a otros lugares a los que se pueda acceder públicamente. (Para obtener un ejemplo básico de un contrato de datos, vea How to: Create a Basic Data Contract for a Class or Structure). WCF controla la definición de los mensajes SOAP subyacentes que permiten la funcionalidad de la operación, así como la serialización de los tipos de datos dentro y fuera del cuerpo de los mensajes. Siempre que los tipos de datos sean serializables, no es necesario pensar en la infraestructura de intercambio de mensajes subyacente al diseñar las operaciones.
Aunque la aplicación WCF típica usa los DataContractAttribute atributos y DataMemberAttribute para crear contratos de datos para las operaciones, puede usar otros mecanismos de serialización. Todos los mecanismos estándar ISerializable, SerializableAttributey IXmlSerializable funcionan para controlar la serialización de los tipos de datos en los mensajes SOAP subyacentes que los llevan de una aplicación a otra. Puede emplear más estrategias de serialización si los tipos de datos requieren soporte especial. Para obtener más información sobre las opciones para la serialización de tipos de datos en aplicaciones WCF, vea Especificar transferencia de datos en contratos de servicio.
Asignar los parámetros y los valores devueltos a los intercambios de mensajes
Las operaciones de servicio están respaldadas por un intercambio subyacente de mensajes SOAP que transfieren datos de ida y vuelta, además de los datos requeridos por la aplicación para admitir ciertas características estándar relacionadas con la seguridad, las transacciones y las sesiones. Dado que este es el caso, la firma de una operación de servicio dicta un determinado patrón de intercambio de mensajes subyacente (MEP) que puede admitir la transferencia de datos y las características que requiere una operación. Puede especificar tres patrones en el modelo de programación WCF: solicitud/respuesta, unidireccional y patrones de mensajes dúplex.
Solicitud/respuesta
Un patrón de solicitud/respuesta es uno en el que un remitente de solicitud (una aplicación cliente) recibe una respuesta con la que se correlaciona la solicitud. Este es el MEP predeterminado porque admite una operación en la que se pasan uno o varios parámetros a la operación y se devuelve un resultado al iniciador de la llamada. Por ejemplo, el siguiente ejemplo de código de C# muestra una operación de servicio básica que toma una cadena y devuelve una cadena.
[OperationContractAttribute]
string Hello(string greeting);
A continuación se muestra el código equivalente de Visual Basic.
<OperationContractAttribute()>
Function Hello (ByVal greeting As String) As String
Esta firma de operación dicta la forma de intercambio de mensajes subyacente. Si no existe ninguna correlación, WCF no puede determinar para qué operación se pretende el valor devuelto.
Tenga en cuenta que, a menos que especifique un patrón de mensaje subyacente diferente, incluso las operaciones de servicio que devuelven void
(Nothing
en Visual Basic) son intercambios de mensajes de solicitud y respuesta. El resultado de la operación es que, a menos que un cliente invoque la operación de forma asincrónica, el cliente detiene el procesamiento hasta que se recibe el mensaje devuelto, aunque ese mensaje esté vacío en el caso normal. En el siguiente ejemplo de código de C# se muestra una operación que no devuelve hasta que el cliente ha recibido un mensaje vacío en respuesta.
[OperationContractAttribute]
void Hello(string greeting);
A continuación se muestra el código equivalente de Visual Basic.
<OperationContractAttribute()>
Sub Hello (ByVal greeting As String)
El ejemplo anterior puede ralentizar el rendimiento y la capacidad de respuesta del cliente si la operación tarda mucho tiempo en realizarse, pero hay ventajas para las operaciones de solicitud y respuesta incluso cuando devuelven void
. La más obvia es que los errores SOAP pueden ser devueltos en el mensaje de respuesta, lo que indica que ha ocurrido alguna condición de error relacionada con el servicio, ya sea en la comunicación o en el procesamiento. Los errores soap especificados en un contrato de servicio se pasan a la aplicación cliente como un FaultException<TDetail> objeto, donde el parámetro type es el tipo especificado en el contrato de servicio. Esto facilita la notificación a los clientes sobre las condiciones de error en los servicios WCF. Para obtener más información sobre las excepciones, los errores soap y el control de errores, consulte Especificación y control de errores en contratos y servicios. Para ver un ejemplo de un servicio de solicitud/respuesta y un cliente, consulte How to: Create a Request-Reply Contract. Para obtener más información sobre los problemas con el patrón de solicitud-respuesta, consulte Request-Reply Services.
Unidireccional
Si el cliente de una aplicación de servicio WCF no debe esperar a que se complete la operación y no procese errores soap, la operación puede especificar un patrón de mensaje unidireccional. Una operación unidireccional es una en la que un cliente invoca una operación y continúa procesando después de que WCF escriba el mensaje en la red. Normalmente esto significa que, a menos que los datos que se envíen en el mensaje saliente sean extremadamente grandes, el cliente continúa ejecutándose casi inmediatamente (a menos que se produzca un error al enviar los datos). Este tipo de patrón de intercambio de mensajes admite un comportamiento de tipo evento desde un cliente hacia una aplicación de servicio.
Un intercambio de mensajes en el que se envía un mensaje y ninguno se recibe no puede admitir una operación de servicio que especifique un valor devuelto distinto de void
; en este caso se produce una excepción InvalidOperationException.
Ningún mensaje devuelto también significa que no se puede devolver ningún error SOAP para indicar errores en el procesamiento o la comunicación. (Comunicar información de error cuando las operaciones son operaciones unidireccionales requieren un patrón de intercambio de mensajes dúplex).
Para especificar un mensaje unidireccional para una operación que devuelve void
, establezca la propiedad IsOneWay en true
, tal como se muestra en el siguiente ejemplo de código C#.
[OperationContractAttribute(IsOneWay=true)]
void Hello(string greeting);
A continuación se muestra el código equivalente de Visual Basic.
<OperationContractAttribute(IsOneWay := True)>
Sub Hello (ByVal greeting As String)
Este método es idéntico al ejemplo de solicitud/respuesta anterior, pero establecer la IsOneWay propiedad en true
significa que, aunque el método es idéntico, la operación de servicio no envía un mensaje devuelto y los clientes devuelven inmediatamente una vez que el mensaje saliente se ha entregado a la capa de canal. Para obtener un ejemplo, vea How to: Create a One-Way Contract. Para obtener más información sobre el patrón unidireccional, consulte One-Way Services.
Dúplex
Un patrón dúplex se caracteriza por la capacidad tanto del servicio como del cliente de enviar mensajes entre sí de forma independiente, ya sea mediante mensajería unidireccional o de solicitud/respuesta. Esta forma de comunicación bidireccional es útil para los servicios que deben comunicarse directamente con el cliente o para ofrecer una experiencia asincrónica para ambas partes de un intercambio de mensajes, incluyendo un comportamiento similar al de un evento.
El patrón dúplex es ligeramente más complejo que los patrones de solicitud/respuesta o unidireccional debido al mecanismo adicional para comunicarse con el cliente.
Para diseñar un contrato dúplex, también debe diseñar un contrato de devolución de llamada y asignar el tipo de ese contrato de devolución de llamada a la propiedad CallbackContract del atributo ServiceContractAttribute que marca el contrato de servicio.
Para implementar un patrón dúplex, es necesario crear una segunda interfaz que contenga las declaraciones de métodos que se invocan en el cliente.
Para obtener un ejemplo de creación de un servicio y un cliente que accede a ese servicio, vea How to: Create a Duplex Contract and How to: Access Services with a Duplex Contract. Para obtener un ejemplo de trabajo, consulte Dúplex. Para obtener más información sobre los problemas relacionados con contratos dúplex, consulte Servicios dúplex.
Precaución
Cuando un servicio recibe un mensaje dúplex, examina el ReplyTo
elemento de ese mensaje entrante para determinar dónde enviar la respuesta. Si el canal que se usa para recibir el mensaje no está protegido, un cliente que no es de confianza podría enviar un mensaje malintencionado a la máquina de destino ReplyTo
, provocando una denegación de servicio (DOS) de esa máquina de destino.
Parámetros out y ref
En la mayoría de los casos, puede usar los parámetros de in
(ByVal
en Visual Basic) y los parámetros de out
y ref
(ByRef
en Visual Basic). Dado que ambos out
parámetros y ref
indican que los datos se devuelven de una operación, una firma de operación, como la siguiente, especifica que se requiere una operación de solicitud o respuesta aunque la firma de la operación devuelva void
.
[ServiceContractAttribute]
public interface IMyContract
{
[OperationContractAttribute]
public void PopulateData(ref CustomDataType data);
}
A continuación se muestra el código equivalente de Visual Basic.
<ServiceContractAttribute()> _
Public Interface IMyContract
<OperationContractAttribute()> _
Public Sub PopulateData(ByRef data As CustomDataType)
End Interface
Las únicas excepciones son aquellos casos en los que la firma tiene una estructura determinada. Por ejemplo, puede usar el NetMsmqBinding enlace para comunicarse con los clientes solo si el método usado para declarar una operación devuelve void
; no puede haber ningún valor de salida, ya sea un valor de retorno, ref
o un parámetro out
.
Además, el uso out
de parámetros o ref
requiere que la operación tenga un mensaje de respuesta subyacente para devolver el objeto modificado. Si la operación es una operación unidireccional, se produce una InvalidOperationException excepción en tiempo de ejecución.
Especificar el nivel de protección de mensajes en el contrato
Al diseñar el contrato, también debe decidir el nivel de protección de mensajes de los servicios que implementan el contrato. Esto solo es necesario si la seguridad del mensaje se aplica al enlace en el extremo del contrato. Si el enlace tiene la seguridad desactivada (es decir, si el enlace proporcionado por el sistema establece el System.ServiceModel.SecurityMode en el valor SecurityMode.None), no tiene que decidir el nivel de protección de los mensajes del contrato. En la mayoría de los casos, los enlaces proporcionados por el sistema con seguridad de nivel de mensaje aplicado proporcionan un nivel de protección suficiente y no es necesario tener en cuenta el nivel de protección para cada operación o para cada mensaje.
El nivel de protección es un valor que especifica si los mensajes (o elementos de mensaje) que admiten un servicio están firmados, firmados y cifrados, o se envían sin firmas ni cifrado. El nivel de protección se puede establecer en varios ámbitos: en el nivel de servicio, para una operación determinada, para un mensaje dentro de esa operación o en una parte de mensaje. Los valores establecidos en un ámbito se convierten en el valor predeterminado para ámbitos más pequeños a menos que se invaliden explícitamente. Si una configuración de enlace no puede proporcionar el nivel de protección mínimo necesario para el contrato, se produce una excepción. Y cuando no se establecen explícitamente valores de nivel de protección en el contrato, la configuración de enlace controla el nivel de protección de todos los mensajes si el enlace tiene seguridad de mensajes. Este es el comportamiento predeterminado.
Importante
Decidir si establecer explícitamente varios ámbitos de un contrato en menos que el nivel de protección completo de ProtectionLevel.EncryptAndSign es generalmente una decisión que negocia algún grado de seguridad para aumentar el rendimiento. En estos casos, las decisiones deben girar en torno a las operaciones y el valor de los datos que intercambian. Para obtener más información, consulte Seguridad de servicios.
Por ejemplo, el siguiente ejemplo de código no establece ni la propiedad ProtectionLevel ni la ProtectionLevel en el contrato.
[ServiceContract]
public interface ISampleService
{
[OperationContractAttribute]
public string GetString();
[OperationContractAttribute]
public int GetInt();
}
A continuación se muestra el código equivalente de Visual Basic.
<ServiceContractAttribute()> _
Public Interface ISampleService
<OperationContractAttribute()> _
Public Function GetString()As String
<OperationContractAttribute()> _
Public Function GetData() As Integer
End Interface
Al interactuar con una implementación en un ISampleService
punto de conexión con un valor predeterminado WSHttpBinding (el valor predeterminado System.ServiceModel.SecurityMode, que es Message), todos los mensajes se cifran y firman porque se trata del nivel de protección predeterminado. Sin embargo, cuando se usa un ISampleService
servicio con un valor predeterminado (el predeterminado BasicHttpBindingSecurityMode, que es None), todos los mensajes se envían como texto porque no hay ninguna seguridad para este enlace y, por tanto, se omite el nivel de protección (es decir, los mensajes no están cifrados ni firmados).
SecurityMode Si se cambió a Message, estos mensajes se cifrarán y firmarán (ya que ahora sería el nivel de protección predeterminado del enlace).
Si desea especificar o ajustar explícitamente los requisitos de protección del contrato, establezca la ProtectionLevel propiedad (o cualquiera de las ProtectionLevel
propiedades en un ámbito más pequeño) en el nivel que requiere el contrato de servicio. En este caso, el uso de una configuración explícita requiere que el enlace admita esa configuración como mínimo para el ámbito utilizado. Por ejemplo, en el ejemplo de código siguiente se especifica explícitamente un ProtectionLevel valor para la GetGuid
operación.
[ServiceContract]
public interface IExplicitProtectionLevelSampleService
{
[OperationContractAttribute]
public string GetString();
[OperationContractAttribute(ProtectionLevel=ProtectionLevel.None)]
public int GetInt();
[OperationContractAttribute(ProtectionLevel=ProtectionLevel.EncryptAndSign)]
public int GetGuid();
}
A continuación se muestra el código equivalente de Visual Basic.
<ServiceContract()> _
Public Interface IExplicitProtectionLevelSampleService
<OperationContract()> _
Public Function GetString() As String
End Function
<OperationContract(ProtectionLevel := ProtectionLevel.None)> _
Public Function GetInt() As Integer
End Function
<OperationContractAttribute(ProtectionLevel := ProtectionLevel.EncryptAndSign)> _
Public Function GetGuid() As Integer
End Function
End Interface
Un servicio que implementa este IExplicitProtectionLevelSampleService
contrato y tiene un punto de conexión que usa el valor predeterminado WSHttpBinding (el predeterminado System.ServiceModel.SecurityMode, que es Message) tiene el siguiente comportamiento:
Los mensajes de operación
GetString
están cifrados y firmados.Los
GetInt
mensajes de operación se envían como texto sin cifrar y sin firmar (es decir, sin formato).La
GetGuid
operación System.Guid se devuelve en un mensaje cifrado y firmado.
Para obtener más información sobre los niveles de protección y cómo usarlos, consulte Descripción del nivel de protección. Para obtener más información sobre la seguridad, consulte Protección de servicios.
Otros requisitos de firma de operación
Algunas características de la aplicación requieren un tipo determinado de firma de operación. Por ejemplo, el enlace NetMsmqBinding soporta los servicios duraderos y clientes, en los que una aplicación se puede reiniciar en el medio de la comunicación y se puede retomar donde se dejó sin olvidarse ningún mensaje. (Para más información, consulte Colas en WCF). Sin embargo, las operaciones duraderas solo deben tomar un parámetro in
y no tienen ningún valor devuelto.
Otro ejemplo es el uso de los tipos Stream en operaciones. Dado que el Stream parámetro incluye todo el cuerpo del mensaje, si una entrada o una salida (es decir, ref
parámetro, out
parámetro o valor devuelto) es de tipo Stream, debe ser la única entrada o salida especificada en la operación. Además, el parámetro o el tipo de valor devuelto deben ser Stream, System.ServiceModel.Channels.Messageo System.Xml.Serialization.IXmlSerializable. Para más información sobre las secuencias, consulte Datos de gran tamaño y secuencias.
Nombres, espacios de nombres y ofuscación
Los nombres y espacios de nombres de los tipos de .NET en la definición de contratos y operaciones son significativos cuando los contratos se convierten en WSDL y cuando los mensajes de contrato se crean y envían. Por lo tanto, se recomienda encarecidamente que los nombres de contrato de servicio y los espacios de nombres se establezcan explícitamente mediante las propiedades Name
y Namespace
de todos los atributos de contrato auxiliares, como las ServiceContractAttribute, OperationContractAttribute, DataContractAttribute, DataMemberAttribute, y otros atributos de contrato.
Una de las consecuencias es que, si no se establecen explícitamente los nombres y espacios de nombres, el uso de la ofuscación de IL en el ensamblado modifica los nombres y espacios de nombres del tipo de contrato y, por lo tanto, los intercambios de conexión y WSDL modificados generan errores. Si no establece explícitamente los nombres de contrato y los espacios de nombres, pero piensa usar ofuscación, use los atributos `ObfuscationAttribute` y `ObfuscateAssemblyAttribute` para evitar la modificación de los nombres de tipo de contrato y los espacios de nombres.
Consulte también
- Cómo crear un contrato Request-Reply
- Cómo crear un contrato de One-Way
- Procedimiento: Crear un contrato dúplex
- Especificación de la transferencia de datos en contratos de servicio
- Especificar y controlar errores en contratos y servicios
- Uso de sesiones
- Operaciones sincrónicas y asincrónicas
- Reliable Services
- Servicios y transacciones