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.
El ejemplo AdvancedDispatchByBody muestra cómo implementar un algoritmo alternativo para asignar mensajes entrantes a operaciones.
De forma predeterminada, el distribuidor del modelo de servicio selecciona el método de control adecuado para un mensaje entrante en función del encabezado WS-Addressing "Acción" del mensaje o la información equivalente en la solicitud SOAP HTTP.
Algunas pilas de servicios Web del SOAP 1.1 que no siguen las instrucciones del WS-I Basic Profile 1.1 no envían mensajes basados en el URI Acción, sino basados en el nombre completo de XML del primer elemento dentro del cuerpo de SOAP. Igualmente, la parte del cliente de estas pilas podría enviar los mensajes con un encabezado de Http SoapAction vacío o arbitrario, el cual se permitió por la especificación SOAP 1.1.
Para cambiar la forma en que los mensajes se envían a métodos, el ejemplo implementa la IDispatchOperationSelector interfaz de extensibilidad en .DispatchByBodyElementOperationSelector
Esta clase selecciona las operaciones en función del primer elemento del cuerpo del mensaje.
El constructor de clase espera un diccionario lleno con pares de XmlQualifiedName
y cadenas, con los que los nombres completos indiquen el nombre del primer elemento secundario del cuerpo de SOAP y que las cadenas indiquen el nombre de la operación correspondiente.
defaultOperationName
es el nombre de la operación que recibe todos los mensajes que no se pueden comparar con este diccionario:
class DispatchByBodyElementOperationSelector : IDispatchOperationSelector
{
Dictionary<XmlQualifiedName, string> dispatchDictionary;
string defaultOperationName;
public DispatchByBodyElementOperationSelector(Dictionary<XmlQualifiedName,string> dispatchDictionary, string defaultOperationName)
{
this.dispatchDictionary = dispatchDictionary;
this.defaultOperationName = defaultOperationName;
}
}
IDispatchOperationSelector Las implementaciones son muy sencillas de compilar, ya que solo hay un método en la interfaz: SelectOperation. El trabajo de este método es inspeccionar un mensaje entrante y devolver una cadena que sea igual al nombre de un método en el contrato de servicio para el punto de conexión actual.
En este ejemplo, el selector de operaciones adquiere un XmlDictionaryReader para el cuerpo del mensaje entrante mediante GetReaderAtBodyContents. Este método ya coloca al lector en el primer elemento secundario del cuerpo del mensaje para que sea suficiente obtener el nombre del elemento vigente y el espacio de nombres URI y combinarlos en XmlQualifiedName
que se utiliza a continuación para buscar la operación correspondiente del diccionario contenida por el selector de la operación.
public string SelectOperation(ref System.ServiceModel.Channels.Message message)
{
XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
XmlQualifiedName lookupQName = new
XmlQualifiedName(bodyReader.LocalName, bodyReader.NamespaceURI);
message = CreateMessageCopy(message,bodyReader);
if (dispatchDictionary.ContainsKey(lookupQName))
{
return dispatchDictionary[lookupQName];
}
else
{
return defaultOperationName;
}
}
El acceso al cuerpo del mensaje con GetReaderAtBodyContents o cualquiera de los demás métodos que proporcionan acceso al contenido del cuerpo del mensaje hace que el mensaje se marque como "lectura", lo que significa que el mensaje no es válido para cualquier procesamiento adicional. Por lo tanto, el selector de operaciones crea una copia del mensaje entrante con el método que se muestra en el código siguiente. Dado que la posición del lector no se ha cambiado durante la inspección, se puede hacer referencia a ella mediante el mensaje recién creado al que también se copian las propiedades del mensaje y los encabezados del mensaje, lo que da como resultado un clon exacto del mensaje original:
private Message CreateMessageCopy(Message message,
XmlDictionaryReader body)
{
Message copy = Message.CreateMessage(message.Version,message.Headers.Action,body);
copy.Headers.CopyHeaderFrom(message,0);
copy.Properties.CopyProperties(message.Properties);
return copy;
}
Agregar un selector de operaciones a un servicio
Los selectores de operaciones de distribución de servicios son extensiones del distribuidor de Windows Communication Foundation (WCF). Para seleccionar los métodos en el canal de devolución de llamada de contratos dúplex, hay también selectores de operación de cliente que trabajan de manera muy similar a los selectores de operación de expedición aquí, pero que no se cubren explícitamente en este ejemplo.
Como la mayoría de las extensiones ejemplares de los servicios, los selectores de la operación de expedición se agregan al distribuidor utilizando los comportamientos. Un comportamiento es un objeto de configuración, que agrega una o varias extensiones al tiempo de ejecución de distribución (o al tiempo de ejecución del cliente) o cambia su configuración.
Dado que los selectores de la operación tienen el ámbito del contrato, el comportamiento adecuado para implementar aquí es IContractBehavior. Dado que la interfaz se implementa en una Attribute clase derivada como se muestra en el código siguiente, el comportamiento se puede agregar declarativamente a cualquier contrato de servicio. Cuando se abre unServiceHost y el tiempo de ejecución de la expedición está generado, todos los comportamientos buscados, bien como atributos en los contratos, operaciones o implementaciones del servicio, o bien como elemento en la configuración del servicio se agregan automáticamente y como consecuencia solicitan contribuir con las extensiones o modificar la configuración predeterminada.
Por motivos de brevedad, el fragmento de código siguiente solo muestra la implementación del método ApplyDispatchBehavior, que afecta a los cambios de configuración del distribuidor en este ejemplo. No se muestran los otros métodos porque vuelven al llamador sin hacer ningún trabajo.
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]
class DispatchByBodyElementBehaviorAttribute : Attribute, IContractBehavior
{
// public void AddBindingParameters(...)
// public void ApplyClientBehavior(...)
// public void Validate(...)
Primero, la implementación ApplyDispatchBehavior prepara el diccionario de búsqueda para el selector de la operación procesa una iteración sobre los elementos OperationDescription en ContractDescriptiondel punto de conexión de servicio. A continuación, cada descripción de la operación se inspecciona para la presencia del DispatchBodyElementAttribute
comportamiento, una implementación de IOperationBehavior que también se define en este ejemplo. Aunque esta clase también es un comportamiento, es pasiva y no contribuye activamente a ningún cambio de configuración en el tiempo de ejecución de distribución. Todos sus métodos vuelven al llamador sin tomar ninguna medida. El comportamiento de la operación solo existe para que los metadatos necesarios para el nuevo mecanismo de distribución, es decir, el nombre completo del elemento body en cuya aparición se seleccione una operación, se puedan asociar a las operaciones respectivas.
Si se encuentra este comportamiento, se agrega un par de valores creado a partir del nombre completo XML (QName
propiedad) y el nombre de la operación (Name
propiedad) al diccionario.
Una vez rellenado el diccionario, un nuevo DispatchByBodyElementOperationSelector
se construye con esta información y se establece como el selector de la operación de la expedición en tiempo de ejecución:
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
{
Dictionary<XmlQualifiedName,string> dispatchDictionary =
new Dictionary<XmlQualifiedName,string>();
foreach( OperationDescription operationDescription in
contractDescription.Operations )
{
DispatchBodyElementAttribute dispatchBodyElement =
operationDescription.Behaviors.Find<DispatchBodyElementAttribute>();
if ( dispatchBodyElement != null )
{
dispatchDictionary.Add(dispatchBodyElement.QName,
operationDescription.Name);
}
}
dispatchRuntime.OperationSelector =
new DispatchByBodyElementOperationSelector(
dispatchDictionary,
dispatchRuntime.UnhandledDispatchOperation.Name);
}
}
Implementación del servicio
El comportamiento implementado en esta muestra afecta directamente a cómo se interpretan y despachan los mensajes del cable, que es una función del contrato de servicio. Por lo tanto, el comportamiento debe declararse en el nivel de contrato de servicio en cualquier implementación de servicio que decida usarla.
El servicio del proyecto del ejemplo aplica el comportamiento de contrato DispatchByBodyElementBehaviorAttribute
al contrato de servicio IDispatchedByBody
y etiqueta cada una de las operaciones OperationForBodyA()
y OperationForBodyB()
con un comportamiento de operación DispatchBodyElementAttribute
. Cuando se abre un host de servicio para un servicio que implementa este contrato, el generador del distribuidor recoge estos metadatos como se ha descrito anteriormente.
Dado que el selector de operaciones envía únicamente basándose en el cuerpo del mensaje y omite "Action", es necesario indicar al entorno de ejecución que no verifique el encabezado "Action" en las respuestas devueltas asignando el comodín "*" a la ReplyAction
propiedad de OperationContractAttribute. Además, se exige tener una operación predeterminada que haya establecido la propiedad “Acción” para el comodín "*." La operación predeterminada recibe todos los mensajes que no se pueden enviar y no tienen DispatchBodyElementAttribute
:
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples"),
DispatchByBodyElementBehavior]
public interface IDispatchedByBody
{
[OperationContract(ReplyAction="*"),
DispatchBodyElement("bodyA","http://tempuri.org")]
Message OperationForBodyA(Message msg);
[OperationContract(ReplyAction = "*"),
DispatchBodyElement("bodyB", "http://tempuri.org")]
Message OperationForBodyB(Message msg);
[OperationContract(Action="*", ReplyAction="*")]
Message DefaultOperation(Message msg);
}
La implementación del servicio de ejemplo es sencilla. Cada método envuelve el mensaje recibido en un mensaje de respuesta y lo envía de vuelta al cliente.
Ejecutar y compilar el ejemplo
Al ejecutar el ejemplo, el contenido del cuerpo de las respuestas de la operación se muestra en la ventana de la consola del cliente de una manera parecida al resultado siguiente (formateado).
El cliente envía tres mensajes al servicio cuyo elemento de contenido del cuerpo se denomina bodyA
, bodyB
y bodyX
, respectivamente. Como se puede inferir de la descripción anterior y del contrato de servicio que se muestra, el mensaje entrante con el elemento bodyA
se envía al método OperationForBodyA()
. Dado que no hay ningún destino de envío explícito para el mensaje con el bodyX
elemento body, el mensaje se envía a .DefaultOperation()
Cada una de las operaciones de servicio encapsula el cuerpo del mensaje recibido en un elemento específico del método y lo devuelve, que se realiza para correlacionar los mensajes de entrada y salida claramente para este ejemplo:
<?xml version="1.0" encoding="IBM437"?>
<replyBodyA xmlns="http://tempuri.org">
<q:bodyA xmlns:q="http://tempuri.org">test</q:bodyA>
</replyBodyA>
<?xml version="1.0" encoding="IBM437"?>
<replyBodyB xmlns="http://tempuri.org">
<q:bodyB xmlns:q="http://tempuri.org">test</q:bodyB>
</replyBodyB>
<?xml version="1.0" encoding="IBM437"?>
<replyDefault xmlns="http://tempuri.org">
<q:bodyX xmlns:q="http://tempuri.org">test</q:bodyX>
</replyDefault>
Para configurar, compilar y ejecutar el ejemplo
Asegúrese de que ha realizado el procedimiento de instalación única para los ejemplos de Windows Communication Foundation.
Para compilar la solución, siga las instrucciones que se indican en Compilación de los ejemplos de Windows Communication Foundation.
Para ejecutar el ejemplo en una configuración de una máquina única o entre máquinas, siga las instrucciones de Ejecución de los ejemplos de Windows Communication Foundation.