Compartir a través de


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ámetro ref.

  • 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, Messageo 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 GetReaderAtBodyContentsy 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.