Uso de la clase de mensajes
La clase Message es fundamental para Windows Communication Foundation (WCF). Toda la comunicación entre clientes y servicios, en última instancia, produce como resultado instancias Message que se envían y reciben.
Normalmente no interactuaría directamente con la clase Message. En su lugar, construcciones de modelo de servicio WCF, como contratos de datos, contratos del mensaje y contratos de operación, se utilizan para describir los mensajes de entrada y salida. Sin embargo, en algunos escenarios avanzados puede programar utilizando directamente la clase Message. Por ejemplo, es posible que desee usar la clase Message:
Cuando necesite una manera alternativa de crear el contenido del mensaje saliente (por ejemplo, crear un mensaje directamente a partir de un archivo en disco) en lugar de serializar los objetos de .NET Framework.
Cuando necesite una manera alternativa de utilizar el contenido del mensaje de entrada (por ejemplo, al desear aplicar una transformación XSLT al contenido de XML sin formato) en lugar de deserializar en los objetos de .NET Framework.
Cuando necesite tratar con mensajes de una manera general sin tener en cuenta el contenido del mensaje (por ejemplo, cuando enrute o reenvíe mensajes al crear un enrutador, equilibrador de carga o un sistema publicación-suscripción
Antes de usar la clase Message, familiarícese con la arquitectura de transferencia de datos de WCF en Información general sobre la arquitectura de transferencia de datos.
Message es un contenedor de uso general para los datos, pero su diseño sigue estrechamente el diseño de un mensaje en el protocolo SOAP. Simplemente como en SOAP, un mensaje tiene un cuerpo de mensaje y encabezados. El cuerpo del mensaje contiene los datos de carga reales, mientras que los encabezados contienen los contenedores de datos con nombre adicionales. Las reglas para leer y escribir el cuerpo y los encabezados son diferentes; Por ejemplo, los encabezados están almacenados siempre en memoria búfer y se puede tener acceso a ellos todas las veces que se quiera, mientras que el cuerpo se puede leer solo una vez y puede ser transmitido. Normalmente, cuando se utiliza SOAP, el cuerpo del mensaje está asignado al cuerpo SOAP y los encabezados del mensaje están asignados a los encabezados SOAP.
Utilizar la clase de mensaje en Operaciones
Puede utilizar la clase Message como un parámetro de entrada de una operación, el valor devuelto de una operación, o ambos. Si Message se utiliza en cualquier parte en una operación, se aplican las restricciones siguientes:
La operación no puede tener ningún
out
o parámetroref
.No puede haber más de un parámetro
input
. Si el parámetro está presente, debe ser Mensaje o un tipo de contrato de mensaje.El tipo de valor devuelto debe ser
void
,Message
o un tipo de contrato de mensaje.
El ejemplo de código siguiente contiene un contrato de operación válido.
[ServiceContract]
public interface IMyService
{
[OperationContract]
Message GetData();
[OperationContract]
void PutData(Message m);
}
<ServiceContract()> _
Public Interface IMyService
<OperationContract()> _
Function GetData() As Message
<OperationContract()> _
Sub PutData(ByVal m As Message)
End Interface
Creación de mensajes básicos
La clase Message proporciona métodos de generación estáticos CreateMessage
que se pueden usar para crear mensajes básicos.
Todas las sobrecargas CreateMessage
toman un parámetro de versión de tipo MessageVersion que indica el SOAP y versiones de WS-Addressing para utilizarlas para el mensaje. Si desea utilizar las mismas versiones de protocolo que el mensaje de entrada, puede utilizar la propiedad IncomingMessageVersion en la instancia OperationContext obtenida de la propiedad Current. La mayoría de las sobrecargas CreateMessage
también tienen un parámetro de cadena que indica la acción SOAP que se utilizará en el mensaje. La versión se puede establecer en None
para deshabilitar la generación de la envoltura SOAP; el mensaje solamente se compone del cuerpo.
Crear mensajes a partir de objetos
La sobrecarga CreateMessage
más básica que toma solo una versión y una acción crea un mensaje con un cuerpo vacío. Otra sobrecarga toma un parámetro Object adicional; esto crea un mensaje cuyo cuerpo es la representación serializada del objeto determinado. Utilice DataContractSerializer con configuración predeterminada para la serialización. Si desea utilizar un serializador diferente, o quiere que DataContractSerializer
se configure de manera diferente, utilice la sobrecarga CreateMessage
que también toma un parámetro XmlObjectSerializer
.
Por ejemplo, para devolver un objeto en un mensaje, puede utilizar el siguiente código.
public class MyService1 : IMyService
{
public Message GetData()
{
Person p = new Person();
p.name = "John Doe";
p.age = 42;
MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
return Message.CreateMessage(ver, "GetDataResponse", p);
}
public void PutData(Message m)
{
// Not implemented.
}
}
[DataContract]
public class Person
{
[DataMember] public string name;
[DataMember] public int age;
}
Public Class MyService1
Implements IMyService
Public Function GetData() As Message _
Implements IMyService.GetData
Dim p As New Person()
p.name = "John Doe"
p.age = 42
Dim ver As MessageVersion = _
OperationContext.Current.IncomingMessageVersion
Return Message.CreateMessage(ver, "GetDataResponse", p)
End Function
Public Sub PutData(ByVal m As Message) _
Implements IMyService.PutData
' Not implemented.
End Sub
End Class
<DataContract()> _
Public Class Person
<DataMember()> _
Public name As String
<DataMember()> _
Public age As Integer
End Class
Crear los mensajes a partir de los lectores XML
Hay sobrecargas CreateMessage
que toman XmlReader o XmlDictionaryReader para el cuerpo en lugar de un objeto. En este caso, el cuerpo del mensaje contiene el XML que se obtiene al leer con el lector XML pasado. Por ejemplo, el código siguiente devuelve un mensaje con el contenido del cuerpo leído desde un archivo XML.
public class MyService2 : IMyService
{
public Message GetData()
{
FileStream stream = new FileStream("myfile.xml",FileMode.Open);
XmlDictionaryReader xdr =
XmlDictionaryReader.CreateTextReader(stream,
new XmlDictionaryReaderQuotas());
MessageVersion ver =
OperationContext.Current.IncomingMessageVersion;
return Message.CreateMessage(ver,"GetDataResponse",xdr);
}
public void PutData(Message m)
{
// Not implemented.
}
}
Public Class MyService2
Implements IMyService
Public Function GetData() As Message Implements IMyService.GetData
Dim stream As New FileStream("myfile.xml", FileMode.Open)
Dim xdr As XmlDictionaryReader = _
XmlDictionaryReader.CreateTextReader(stream, New XmlDictionaryReaderQuotas())
Dim ver As MessageVersion = OperationContext.Current.IncomingMessageVersion
Return Message.CreateMessage(ver, "GetDataResponse", xdr)
End Function
Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
End Sub
End Class
Hay además, sobrecargas CreateMessage
que toman XmlReader o XmlDictionaryReader que representa el mensaje completo y no simplemente el cuerpo. Estas sobrecargas también toman un parámetro maxSizeOfHeaders
entero. Los encabezados siempre se almacenan en búfer en la memoria en cuanto se crea el mensaje, y este parámetro limita la cantidad de almacenamiento en búfer que tiene lugar. Es importante establecer este parámetro en un valor seguro si el XML procede de un origen que no es de confianza para mitigar la posibilidad de un ataque por denegación de servicio. El SOAP y versiones de WS-Addressing del mensaje que el lector XML representa deben coincidir con las versiones indicadas utilizando el parámetro de versión.
Crear los mensajes con BodyWriter
Una sobrecarga CreateMessage
toma una instancia BodyWriter
para describir el cuerpo del mensaje. BodyWriter
es una clase abstracta que se puede derivar para personalizar cómo se crean los cuerpos del mensaje. Puede crear su propia clase BodyWriter
derivada para describir los cuerpos del mensaje de una manera personalizada. Debe invalidar el método BodyWriter.OnWriteBodyContents
que toma XmlDictionaryWriter; este método es el responsable de la escritura del cuerpo.
Los sistemas de escritura del cuerpo se pueden almacenar en búfer o no (se pueden transmitir por secuencias). Los sistemas de escritura del cuerpo almacenados en búfer pueden escribir su contenido todas las veces que se quiera, mientras que los transmitidos solo pueden escribir sus contenidos una vez. La propiedad IsBuffered
indica si un sistema de escritura del cuerpo está almacenado en búfer o no. Puede establecerlo para su sistema de escritura de cuerpo mediante una llamada al constructor BodyWriter
protegido que toma un parámetro booleano isBuffered
. Los sistemas de escritura de cuerpo permiten crear un sistema de escritura de cuerpo almacenado en búfer a partir de un sistema de escritura de cuerpo no almacenado en búfer. Puede invalidar el método OnCreateBufferedCopy
para personalizar este proceso. De forma predeterminada, se usa un búfer en memoria que contiene el XML devuelto por OnWriteBodyContents
. OnCreateBufferedCopy
toma un parámetro entero maxBufferSize
; si invalida este método, no debe crear búferes mayores que este tamaño máximo.
La clase BodyWriter
proporciona respectivamente WriteBodyContents
y los métodos CreateBufferedCopy
, que son contenedores esencialmente delgados alrededor de OnWriteBodyContents
y los métodos OnCreateBufferedCopy
. Estos métodos realizan la comprobación de estado para asegurarse de que no se tiene acceso a un sistema de escritura de cuerpo no almacenado en búfer más de una vez. Solo se llama a estos métodos directamente al crear clases Message
derivadas personalizadas basadas en BodyWriters
.
Crear los mensajes de error
Puede utilizar ciertas sobrecargas CreateMessage
para crear los mensajes del error de SOAP. La más básica toma un objeto MessageFault que describe el error. Otras sobrecargas se proporcionan para comodidad. La primera sobrecarga toma FaultCode
y una cadena de la razón y crea MessageFault
mediante MessageFault.CreateFault
mediante esta información. La otra sobrecarga toma un objeto de datos y también lo pasa a CreateFault
junto con el código de error y la razón. Por ejemplo, la operación siguiente devuelve un error.
public class MyService3 : IMyService
{
public Message GetData()
{
FaultCode fc = new FaultCode("Receiver");
MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
return Message.CreateMessage(ver,fc,"Bad data","GetDataResponse");
}
public void PutData(Message m)
{
// Not implemented.
}
}
Public Class MyService3
Implements IMyService
Public Function GetData() As Message Implements IMyService.GetData
Dim fc As New FaultCode("Receiver")
Dim ver As MessageVersion = OperationContext.Current.IncomingMessageVersion
Return Message.CreateMessage(ver, fc, "Bad data", "GetDataResponse")
End Function
Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
End Sub
End Class
Extraer los datos del cuerpo del mensaje
La clase Message
admite varias maneras de extraer información de su cuerpo. Éstos pueden estar clasificados en las categorías siguientes:
Obtener el cuerpo del mensaje completo escrito de una vez en un sistema de escritura de XML. Esto se conoce como escribir un mensaje.
Obtener un lector XML sobre el cuerpo del mensaje. Esto le permite al acceso posterior el cuerpo del mensaje parte por parte, según se requiera. Esto se conoce como leer un mensaje.
El mensaje completo, incluido su cuerpo, se puede copiar en un búfer en memoria del tipo MessageBuffer. Esto se conoce como copiar un mensaje.
Solo puede tener acceso una vez al cuerpo de Message
, independientemente de cómo se tiene acceso. Un objeto de mensaje tiene una propiedad State
, la cual se establece inicialmente en Creada. Los tres métodos de acceso descritos en la lista anterior establecen el estado en Escrito, Leído, y Copiado, respectivamente. Además, un método Close
puede establecer el estado en Cerrado cuando ya no se necesita el contenido del cuerpo del mensaje. Se puede tener acceso al cuerpo del mensaje solo en el estado Creado y no hay ninguna manera de regresar al estado Creado después de que el estado haya cambiado.
Escribir los mensajes
El método WriteBodyContents(XmlDictionaryWriter) escribe el contenido del cuerpo de un Message
determinado cree instancias a un sistema de escritura XML determinado. El método WriteBody hace lo mismo, salvo que agrega el contenido del cuerpo en el elemento contenedor adecuado (por ejemplo, <soap:body>
). Finalmente, WriteMessage escribe el mensaje completo, incluso la envoltura SOAP de ajuste y los encabezados. Si SOAP está desactivado (Version es MessageVersion.None), los tres métodos hacen lo mismo: escriben el contenido del cuerpo del mensaje.
Por ejemplo, el código siguiente escribe el cuerpo de un mensaje de entrada en un archivo.
public class MyService4 : IMyService
{
public void PutData(Message m)
{
FileStream stream = new FileStream("myfile.xml",FileMode.Create);
XmlDictionaryWriter xdw =
XmlDictionaryWriter.CreateTextWriter(stream);
m.WriteBodyContents(xdw);
xdw.Flush();
}
public Message GetData()
{
throw new NotImplementedException();
}
}
Public Class MyService4
Implements IMyService
Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
Dim stream As New FileStream("myfile.xml", FileMode.Create)
Dim xdw As XmlDictionaryWriter = XmlDictionaryWriter.CreateTextWriter(stream)
m.WriteBodyContents(xdw)
xdw.Flush()
End Sub
Public Function GetData() As Message Implements IMyService.GetData
Throw New NotImplementedException()
End Function
End Class
Dos métodos del asistente adicionales escriben ciertas etiquetas de elemento de inicio de SOAP. Estos métodos no tienen acceso al cuerpo del mensaje, de modo que no cambian el estado del mensaje. Entre ellas se incluyen las siguientes:
WriteStartBody escribe el elemento de cuerpo de inicio, por ejemplo,
<soap:Body>
.WriteStartEnvelope escribe el elemento de envoltura de inicio, por ejemplo,
<soap:Envelope>
.
Para escribir las etiquetas de elementos finales correspondientes, llame WriteEndElement
en el sistema de escritura XML correspondiente. Rara vez se llama a estos métodos directamente.
Leer mensajes
La manera principal para leer un cuerpo del mensaje es llamar GetReaderAtBodyContents. Se recibe XmlDictionaryReader, que se puede usar para leer el cuerpo del mensaje. Tenga en cuenta que Message pasa al estado Leído en cuanto se llama a GetReaderAtBodyContents y no cuando se usa el lector XML devuelto.
El método GetBody también le permite tener acceso al cuerpo del mensaje como un objeto con tipo. Internamente, este método utiliza GetReaderAtBodyContents
y así también pasa el estado del mensaje al estado Read (vea la propiedad State ).
Es recomendable comprobar la propiedad IsEmpty, en cuyo caso el cuerpo del mensaje está vacío y GetReaderAtBodyContents inicia InvalidOperationException. Además, si es un mensaje recibido (por ejemplo, la respuesta), es posible que también desee comprobar IsFault, que indica si el mensaje contiene un error.
La sobrecarga más básica de GetBody deserializa el cuerpo del mensaje en una instancia de un tipo (indicado por el parámetro genérico) mediante el uso de un DataContractSerializer establecido con la configuración predeterminada y con la cuota MaxItemsInObjectGraph deshabilitada. Si desea usar un motor de serialización diferente o configurar DataContractSerializer
de una manera distinta a la predeterminada, use la sobrecarga GetBody que toma XmlObjectSerializer.
Por ejemplo, el código siguiente extrae los datos de un cuerpo de mensaje que contiene un objeto Person
serializado e imprime el nombre de la persona.
public class MyService5 : IMyService
{
public void PutData(Message m)
{
Person p = m.GetBody<Person>();
Console.WriteLine(p.name);
}
public Message GetData()
{
throw new NotImplementedException();
}
}
}
namespace Samples2
{
[ServiceContract]
public interface IMyService
{
[OperationContract]
Message GetData();
[OperationContract]
void PutData(Message m);
}
[DataContract]
public class Person
{
[DataMember] public string name;
[DataMember] public int age;
}
Public Class MyService5
Implements IMyService
Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
Dim p As Person = m.GetBody(Of Person)()
Console.WriteLine(p.name)
End Sub
Public Function GetData() As Message Implements IMyService.GetData
Throw New NotImplementedException()
End Function
End Class
End Namespace
Namespace Samples2
<ServiceContract()> _
Public Interface IMyService
<OperationContract()> _
Function GetData() As Message
<OperationContract()> _
Sub PutData(ByVal m As Message)
End Interface
<DataContract()> _
Public Class Person
<DataMember()> _
Public name As String
<DataMember()> _
Public age As Integer
End Class
Copiar un mensaje en un búfer
A veces es necesario tener acceso más de una vez al cuerpo del mensaje, por ejemplo, para reenviar el mismo mensaje a varios destinos como parte de un sistema de publicación-suscripción. En este caso, es necesario almacenar en búfer el mensaje completo (incluido el cuerpo) en memoria. Puede hacerlo llamando CreateBufferedCopy(Int32). Este método toma un parámetro entero que representa el tamaño máximo de búfer y crea un búfer inferior a este tamaño. Es importante establecer esto en un valor seguro si el mensaje procede de un origen que no es de confianza.
Se devuelve el búfer como una instancia MessageBuffer. Puede tener acceso a los datos del búfer de varias maneras. El modo principal es llamar CreateMessage para crear las instancias Message
a partir del búfer.
Otra manera de tener acceso a los datos del búfer es implementar la interfaz IXPathNavigable que la clase MessageBuffer implementa para tener acceso directamente al XML subyacente. Algunas sobrecargas CreateNavigator permiten crear navegadores System.Xml.XPath protegidos por una cuota de nodo que limita el número de nodos XML que se pueden visitar. Esto ayuda a evitar ataques por denegación de servicio en función del tiempo de procesamiento largo. Esta cuota está deshabilitada de forma predeterminada. Algunas sobrecargas CreateNavigator
le permiten especificar cómo se debería administrar el espacio en blanco en el XML utilizando la enumeración XmlSpace, con los valores predeterminados XmlSpace.None
.
Una última manera de tener acceso al contenido de un búfer del mensaje es escribir su contenido en una secuencia utilizando WriteMessage.
El ejemplo siguiente muestra el proceso de trabajar con MessageBuffer
: un mensaje de entrada se reenvía a varios destinatarios y, a continuación, se registra en un archivo. Sin el almacenamiento en búfer, esto no es posible, porque se puede tener acceso al cuerpo del mensaje una sola vez.
[ServiceContract]
public class ForwardingService
{
private List<IOutputChannel> forwardingAddresses;
[OperationContract]
public void ForwardMessage (Message m)
{
//Copy the message to a buffer.
MessageBuffer mb = m.CreateBufferedCopy(65536);
//Forward to multiple recipients.
foreach (IOutputChannel channel in forwardingAddresses)
{
Message copy = mb.CreateMessage();
channel.Send(copy);
}
//Log to a file.
FileStream stream = new FileStream("log.xml",FileMode.Append);
mb.WriteMessage(stream);
stream.Flush();
}
}
<ServiceContract()> _
Public Class ForwardingService
Private forwardingAddresses As List(Of IOutputChannel)
<OperationContract()> _
Public Sub ForwardMessage(ByVal m As Message)
'Copy the message to a buffer.
Dim mb As MessageBuffer = m.CreateBufferedCopy(65536)
'Forward to multiple recipients.
Dim channel As IOutputChannel
For Each channel In forwardingAddresses
Dim copy As Message = mb.CreateMessage()
channel.Send(copy)
Next channel
'Log to a file.
Dim stream As New FileStream("log.xml", FileMode.Append)
mb.WriteMessage(stream)
stream.Flush()
End Sub
End Class
La clase MessageBuffer
tiene otro miembros que hay que tener en cuenta. Se puede llamar al método Close para liberar recursos cuando ya no se requiera el contenido del búfer. La propiedad BufferSize devuelve el tamaño del búfer asignado. La propiedad MessageContentType devuelve el tipo de contenido de MIME del mensaje.
Tener acceso al cuerpo del mensaje para depuración
Para la depuración, puede llamar al método ToString con el fin de obtener una representación del mensaje como una cadena. Esta representación por lo general coincide con la manera en que se mostraría un mensaje en la conexión si estuviera codificado con el codificador de texto, con la excepción de que se le aplicaría un formato más legible que XML. La excepción a esto es el cuerpo del mensaje. El cuerpo se puede leer una sola vez y ToString
no cambia el estado del mensaje. Por consiguiente, el método ToString
no podría tener acceso al cuerpo y podría sustituir un marcador de posición (por ejemplo, “…” o tres puntos) en lugar del cuerpo del mensaje. Por consiguiente, no use ToString
para registrar los mensajes si el contenido del cuerpo de los mensajes es importante.
Tener acceso a otras partes del mensaje
Se proporcionan varias propiedades para tener acceso a la información del mensaje que no sea el contenido del cuerpo. Sin embargo, no se puede llamar a éstos una vez se ha cerrado el mensaje:
La propiedad Headers representa los encabezados del mensaje. Consulte la sección en "Trabajar con encabezados" más adelante en este tema.
La propiedad Properties representa las propiedades del mensaje, que son partes de datos con nombre adjuntadas al mensaje que generalmente no se emiten cuando se envía el mensaje. Consulte la sección en "Trabajar con propiedades" más adelante en este tema.
La propiedad Version indica el SOAP y versión WS-Addressing asociada al mensaje o
None
si SOAP está deshabilitado.La propiedad IsFault devuelve
true
si el mensaje es un mensaje de error de SOAP.La propiedad IsEmpty devuelve
true
si el mensaje está vacío.
Puede utilizar el método GetBodyAttribute(String, String) para tener acceso a un atributo determinado en el elemento contenedor del cuerpo (por ejemplo, <soap:Body>
) identificado por un nombre y espacio de nombres determinados. Si no se encuentra dicho atributo, se devuelve null
. Se puede llamar a este método solo cuando Message
está en el estado Creado (cuando todavía no se ha tenido acceso al cuerpo del mensaje).
Trabajar con encabezados
Un Message
puede contener cualquier número de fragmentos XML con nombre, llamados encabezados. Cada fragmento asigna normalmente a un encabezado SOAP. Se tiene acceso a los encabezados a través de la propiedad Headers
de tipo MessageHeaders. MessageHeaders es una colección de objetos MessageHeaderInfo, y se puede tener acceso a los encabezados individuales a través de su interfaz IEnumerable o a través de su indizador. Por ejemplo, el código siguiente hace una lista de los nombres de todos los encabezados en Message
.
public class MyService6 : IMyService
{
public void PutData(Message m)
{
foreach (MessageHeaderInfo mhi in m.Headers)
{
Console.WriteLine(mhi.Name);
}
}
public Message GetData()
{
throw new NotImplementedException();
}
}
Public Class MyService6
Implements IMyService
Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
Dim mhi As MessageHeaderInfo
For Each mhi In m.Headers
Console.WriteLine(mhi.Name)
Next mhi
End Sub
Public Function GetData() As Message Implements IMyService.GetData
Throw New NotImplementedException()
End Function
End Class
Agregar, quitar, buscar encabezados
Puede agregar un nuevo encabezado al final de todos los encabezados existentes utilizando el método Add. Puede utilizar el método Insert para insertar un encabezado en un índice determinado. Los encabezados existentes se desplazan para el elemento insertado. Los encabezados se ordenan según su índice y el primer índice disponible es 0. Puede usar las distintas sobrecargas del método CopyHeadersFrom para agregar encabezados de una instancia de Message
o MessageHeaders
diferente. Algunas sobrecargas copian un encabezado individual, mientras que otras copian todos ellos. El método Clear quita todos los encabezados. El método RemoveAt quita un encabezado en un índice determinado (desplazando todos los encabezados después de él). El método RemoveAll quita todos los encabezados con un nombre y espacio de nombres determinados.
Recupere un encabezado determinado mediante el método FindHeader. Este método toma el nombre y espacio de nombres del encabezado para buscar y devuelve su índice. Si el encabezado se produce más de una vez, se produce una excepción. Si no se encuentra el encabezado, devuelve -1.
En el modelo del encabezado SOAP, los encabezados pueden tener un valor Actor
que especifica el destinatario previsto del encabezado. La sobrecarga FindHeader
más básica solo busca en los encabezados que están pensados para el receptor definitivo del mensaje. Sin embargo, otra sobrecarga le permite especificar qué valores Actor
están incluidos en la búsqueda. Para obtener más información, consulte la especificación SOAP.
Se proporciona un método CopyTo(MessageHeaderInfo[], Int32) para copiar los encabezados de una colección MessageHeaders en una matriz de los objetos MessageHeaderInfo.
Para tener acceso a los datos XML en un encabezado, puede llamar GetReaderAtHeader y devolver un lector XML para el índice del encabezado concreto. Si quiere deserializar el contenido del encabezado en un objeto, utilice GetHeader<T>(Int32) o una de las otras sobrecargas. Las sobrecargas más básicas deserializan los encabezados mediante el uso del DataContractSerializer configurado de la manera predeterminada. Si desea utilizar un serializador diferente o una configuración diferente de DataContractSerializer
, utilice una de las sobrecargas que toman XmlObjectSerializer
. También hay sobrecarga que toma el nombre del encabezado, espacio de nombres y opcionalmente una lista de los valores Actor
en lugar de un índice; ésta es una combinación de FindHeader
y GetHeader
.
Trabajar con propiedades
Una instancia Message
puede contener un número arbitrario de objetos con nombre de tipos arbitrarios. Se accede a esta colección a través de la propiedadProperties
de tipo MessageProperties
. La colección implementa la interfaz IDictionary<TKey,TValue> y actúa como una asignación de String a Object. Normalmente, los valores de propiedad no se asignan directamente a cualquier parte del mensaje de la conexión, sino que proporcionan varias sugerencias de procesamiento de mensajes a los diferentes canales de la pila del canal WCF o al marco del servicio CopyTo(MessageHeaderInfo[], Int32). Para obtener un ejemplo, consulte Información general sobre la arquitectura de transferencia de datos.
Herencia de la clase de mensaje
Si los tipos de mensaje integrados creados mediante CreateMessage
no cumplen sus requisitos, cree una clase que derive de la clase Message
.
Definir el contenido del cuerpo del mensaje
Existen tres técnicas primarias para tener acceso a los datos dentro de un cuerpo del mensaje: escribir, leer y copiar en un búfer. En última instancia, como consecuencia de estas operaciones se llama a los métodos OnWriteBodyContents, OnGetReaderAtBodyContents y OnCreateBufferedCopy, respectivamente, en la clase derivada de Message
. La clase base Message
garantiza que se llamará a uno de estos métodos por cada instancia de Message
y que no se llama más de una vez. La clase base también garantiza que no se llama a los métodos en un mensaje cerrado. No hay ninguna necesidad de realizar el seguimiento del estado del mensaje en su implementación.
OnWriteBodyContents es un método abstracto y se implementa. La manera más básica de definir el contenido del cuerpo de un mensaje es escribirlo con este método. Por ejemplo, el mensaje siguiente contiene 100,000 números aleatorios de 1 a 20.
public class RandomMessage : Message
{
override protected void OnWriteBodyContents(XmlDictionaryWriter writer)
{
Random r = new Random();
for (int i = 0; i <100000; i++)
{
writer.WriteStartElement("number");
writer.WriteValue(r.Next(1,20));
writer.WriteEndElement();
}
}
//code omitted…
Public Class RandomMessage
Inherits Message
Protected Overrides Sub OnWriteBodyContents( _
ByVal writer As XmlDictionaryWriter)
Dim r As New Random()
Dim i As Integer
For i = 0 To 99999
writer.WriteStartElement("number")
writer.WriteValue(r.Next(1, 20))
writer.WriteEndElement()
Next i
End Sub
' Code omitted.
OnGetReaderAtBodyContents() y los métodos OnCreateBufferedCopy tienen implementaciones predeterminadas que funcionan en la mayoría de los casos. Las implementaciones predeterminadas llaman a OnWriteBodyContents, almacenan en búfer los resultados y funcionan con el búfer resultante. Sin embargo, en algunos casos esto puede no ser bastante. En el ejemplo anterior, leer el mensaje resulta en 100,000 elementos XML almacenados en búfer, lo cual podría no ser deseable. Es posible que desee invalidar OnGetReaderAtBodyContents() para devolver una clase XmlDictionaryReader derivada personalizada que proporcione números aleatorios. Puede invalidar a continuación OnWriteBodyContents para utilizar el lector que devuelve el método OnGetReaderAtBodyContents(), tal y como se muestra en el ejemplo siguiente.
public override MessageHeaders Headers
{
get { throw new Exception("The method or operation is not implemented."); }
}
public override MessageProperties Properties
{
get { throw new Exception("The method or operation is not implemented."); }
}
public override MessageVersion Version
{
get { throw new Exception("The method or operation is not implemented."); }
}
}
public class RandomMessage2 : Message
{
override protected XmlDictionaryReader OnGetReaderAtBodyContents()
{
return new RandomNumbersXmlReader();
}
override protected void OnWriteBodyContents(XmlDictionaryWriter writer)
{
XmlDictionaryReader xdr = OnGetReaderAtBodyContents();
writer.WriteNode(xdr, true);
}
public override MessageHeaders Headers
{
get { throw new Exception("The method or operation is not implemented."); }
}
public override MessageProperties Properties
{
get { throw new Exception("The method or operation is not implemented."); }
}
public override MessageVersion Version
{
get { throw new Exception("The method or operation is not implemented."); }
}
}
public class RandomNumbersXmlReader : XmlDictionaryReader
{
//code to serve up 100000 random numbers in XML form omitted…
Public Overrides ReadOnly Property Headers() As MessageHeaders
Get
Throw New Exception("The method or operation is not implemented.")
End Get
End Property
Public Overrides ReadOnly Property Properties() As MessageProperties
Get
Throw New Exception("The method or operation is not implemented.")
End Get
End Property
Public Overrides ReadOnly Property Version() As MessageVersion
Get
Throw New Exception("The method or operation is not implemented.")
End Get
End Property
End Class
Public Class RandomMessage2
Inherits Message
Protected Overrides Function OnGetReaderAtBodyContents() As XmlDictionaryReader
Return New RandomNumbersXmlReader()
End Function
Protected Overrides Sub OnWriteBodyContents(ByVal writer As XmlDictionaryWriter)
Dim xdr As XmlDictionaryReader = OnGetReaderAtBodyContents()
writer.WriteNode(xdr, True)
End Sub
Public Overrides ReadOnly Property Headers() As MessageHeaders
Get
Throw New Exception("The method or operation is not implemented.")
End Get
End Property
Public Overrides ReadOnly Property Properties() As MessageProperties
Get
Throw New Exception("The method or operation is not implemented.")
End Get
End Property
Public Overrides ReadOnly Property Version() As MessageVersion
Get
Throw New Exception("The method or operation is not implemented.")
End Get
End Property
End Class
Public Class RandomNumbersXmlReader
Inherits XmlDictionaryReader
'code to serve up 100000 random numbers in XML form omitted
De igual forma, podría desear invalidar OnCreateBufferedCopy
para devolver su propia clase derivada MessageBuffer
.
Además de proporcionar el contenido del cuerpo del mensaje, la clase derivada del mensaje también debe invalidar las propiedades Version
, Headers
y Properties
.
Tenga en cuenta que si crea una copia de un mensaje, la copia utiliza los encabezados del mensaje del original.
Otros miembros que se pueden invalidar
Puede invalidar los métodos OnWriteStartEnvelope, OnWriteStartHeaders y OnWriteStartBody para especificar cómo se escribe la envoltura SOAP, encabezados SOAP y etiquetas iniciales del elemento de cuerpo SOAP. Estos corresponden normalmente a <soap:Envelope>
, <soap:Header>
y <soap:Body>
. Estos métodos no deberían escribir normalmente nada si la propiedad Version devuelve None.
Nota
La implementación predeterminada de OnGetReaderAtBodyContents
llama OnWriteStartEnvelope
y OnWriteStartBody
antes de llamar OnWriteBodyContents
y almacenar en búfer los resultados. Los encabezados no se escriben.
Invalide el método OnWriteMessage para cambiar la manera de construir el mensaje completo a partir de sus diferentes partes. El método OnWriteMessage
es llamado por WriteMessage y por la implementación predeterminada OnCreateBufferedCopy. Tenga en cuenta que invalidar WriteMessage no es un procedimiento recomendado. Es mejor invalidar los métodos On
adecuados (por ejemplo, OnWriteStartEnvelope, OnWriteStartHeadersy OnWriteBodyContents.
Invalide OnBodyToString para invalidar cómo se representa el cuerpo del mensaje durante la depuración. El valor predeterminado es representarlo con tres puntos ("..."). Tenga en cuenta que se puede llamar a este método varias veces cuando el estado del mensaje es otro distinto de Cerrado. Una implementación de este método no debería producir nunca una acción que solo se deba realizar una vez (como leer de una secuencia solo hacia adelante).
Invalide el método OnGetBodyAttribute para permitir el acceso a los atributos en el elemento de cuerpo SOAP. Este método se puede llamar todas las veces que se desee, pero el tipo base Message
garantiza que solo se llama cuando el mensaje se encuentre en estado Creado. No se requiere para comprobar el estado en una implementación. La implementación predeterminada siempre devuelve null
, lo que indica que no hay ningún atributo en el elemento del cuerpo.
Si el objeto Message
debe llevar a cabo alguna operación de limpieza especial cuando ya no se necesita el cuerpo del mensaje, puede invalidar OnClose. La implementación predeterminada no hace nada.
IsEmpty
y las propiedades IsFault
se pueden invalidar. De forma predeterminada, ambos devuelven false
.