Usando a classe Message
A Message classe é fundamental para o Windows Communication Foundation (WCF). Toda a comunicação entre clientes e serviços resulta, em última análise, no envio e recebimento de Message instâncias.
Normalmente, você não interagiria diretamente com a Message turma. Em vez disso, construções de modelo de serviço WCF, como contratos de dados, contratos de mensagem e contratos de operação, são usadas para descrever mensagens de entrada e saída. No entanto, em alguns cenários avançados, você pode programar usando a Message classe diretamente. Por exemplo, talvez você queira usar a Message classe:
Quando você precisa de uma maneira alternativa de criar conteúdo de mensagem de saída (por exemplo, criar uma mensagem diretamente de um arquivo no disco) em vez de serializar objetos do .NET Framework.
Quando você precisa de uma maneira alternativa de usar o conteúdo da mensagem de entrada (por exemplo, quando você deseja aplicar uma transformação XSLT ao conteúdo XML bruto) em vez de desserializar em objetos do .NET Framework.
Quando você precisa lidar com mensagens de uma maneira geral, independentemente do conteúdo da mensagem (por exemplo, ao rotear ou encaminhar mensagens ao criar um roteador, balanceador de carga ou um sistema de publicação-assinatura).
Antes de usar a Message classe, familiarize-se com a arquitetura de transferência de dados WCF em Visão geral da arquitetura de transferência de dados.
A Message é um contêiner de uso geral para dados, mas seu design segue de perto o design de uma mensagem no protocolo SOAP. Assim como no SOAP, uma mensagem tem um corpo e cabeçalhos. O corpo da mensagem contém os dados reais da carga, enquanto os cabeçalhos contêm contêineres de dados nomeados adicionais. As regras para ler e escrever o corpo e os cabeçalhos são diferentes, por exemplo, os cabeçalhos são sempre armazenados em buffer na memória e podem ser acessados em qualquer ordem qualquer número de vezes, enquanto o corpo pode ser lido apenas uma vez e pode ser transmitido. Normalmente, ao usar SOAP, o corpo da mensagem é mapeado para o corpo SOAP e os cabeçalhos de mensagem são mapeados para os cabeçalhos SOAP.
Usando a classe Message em operações
Você pode usar a Message classe como um parâmetro de entrada de uma operação, o valor de retorno de uma operação ou ambos. Se Message for usado em qualquer lugar em uma operação, as seguintes restrições se aplicam:
A operação não pode ter nenhum
out
ouref
parâmetros.Não pode haver mais de um
input
parâmetro. Se o parâmetro estiver presente, ele deve ser Message ou um tipo de contrato de mensagem.O tipo de
void
retorno deve ser ,Message
ou um tipo de contrato de mensagem.
O exemplo de código a seguir contém um contrato de operação 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
Criação de mensagens básicas
A Message classe fornece métodos estáticos CreateMessage
de fábrica que você pode usar para criar mensagens básicas.
Todas as CreateMessage
sobrecargas usam um parâmetro de versão do tipo MessageVersion que indica as versões SOAP e WS-Addressing a serem usadas para a mensagem. Se você quiser usar as mesmas versões de protocolo que a mensagem de entrada, você pode usar a IncomingMessageVersion OperationContext propriedade na instância obtida da Current propriedade. A maioria das CreateMessage
sobrecargas também tem um parâmetro string que indica a ação SOAP a ser usada para a mensagem. A versão pode ser definida para None
desativar a geração de envelopes SOAP, a mensagem consiste apenas no corpo.
Criando mensagens a partir de objetos
A sobrecarga mais básica CreateMessage
que leva apenas uma versão e uma ação cria uma mensagem com um corpo vazio. Outra sobrecarga usa um parâmetro adicional Object , isso cria uma mensagem cujo corpo é a representação serializada do objeto dado. Use as configurações com padrão DataContractSerializer para serialização. Se você quiser usar um serializador diferente, ou você quiser o DataContractSerializer
configurado de forma diferente, use a CreateMessage
sobrecarga que também usa um XmlObjectSerializer
parâmetro.
Por exemplo, para retornar um objeto em uma mensagem, você pode usar o código a seguir.
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
Criando mensagens a partir de leitores XML
Há CreateMessage
sobrecargas que levam um XmlReader ou um XmlDictionaryReader para o corpo em vez de um objeto. Nesse caso, o corpo da mensagem contém o XML que resulta da leitura do leitor XML passado. Por exemplo, o código a seguir retorna uma mensagem com o conteúdo do corpo lido de um arquivo 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
Além disso, há CreateMessage
sobrecargas que levam um XmlReader ou um XmlDictionaryReader que representa toda a mensagem e não apenas o corpo. Essas sobrecargas também usam um parâmetro inteiro maxSizeOfHeaders
. Os cabeçalhos são sempre armazenados em buffer na memória assim que a mensagem é criada, e esse parâmetro limita a quantidade de buffer que ocorre. É importante definir esse parâmetro como um valor seguro se o XML for proveniente de uma fonte não confiável para mitigar a possibilidade de um ataque de negação de serviço. As versões SOAP e WS-Addressing da mensagem que o leitor XML representa devem corresponder às versões indicadas usando o parâmetro version.
Criando mensagens com o BodyWriter
Uma CreateMessage
sobrecarga leva uma BodyWriter
instância para descrever o corpo da mensagem. A BodyWriter
é uma classe abstrata que pode ser derivada para personalizar como os corpos de mensagem são criados. Você pode criar sua própria BodyWriter
classe derivada para descrever corpos de mensagens de maneira personalizada. Você deve substituir o BodyWriter.OnWriteBodyContents
método que toma um XmlDictionaryWriter, este método é responsável por escrever o corpo.
Os gravadores de corpo podem ser armazenados em buffer ou não-buffered (streamed). Os gravadores de corpo em buffer podem escrever seu conteúdo qualquer número de vezes, enquanto os transmitidos podem escrever seu conteúdo apenas uma vez. A IsBuffered
propriedade indica se um gravador de corpo está armazenado em buffer ou não. Você pode definir isso para seu gravador de corpo chamando o construtor protegido BodyWriter
que usa um isBuffered
parâmetro booleano. Os gravadores de corpo suportam a criação de um gravador de corpo em buffer a partir de um gravador de corpo sem buffer. Você pode substituir o OnCreateBufferedCopy
método para personalizar esse processo. Por padrão, um buffer na memória que contém o XML retornado por OnWriteBodyContents
é usado. OnCreateBufferedCopy
usa um maxBufferSize
parâmetro inteiro, se você substituir esse método, não deverá criar buffers maiores que esse tamanho máximo.
A BodyWriter
classe fornece os WriteBodyContents
métodos e CreateBufferedCopy
, que são essencialmente invólucros finos e OnWriteBodyContents
OnCreateBufferedCopy
métodos, respectivamente. Esses métodos executam a verificação de estado para garantir que um gravador de corpo sem buffer não seja acessado mais de uma vez. Esses métodos são chamados diretamente somente ao criar classes derivadas personalizadas Message
com base em BodyWriters
.
Criação de mensagens de falha
Você pode usar determinadas CreateMessage
sobrecargas para criar mensagens de falha SOAP. O mais básico deles leva um MessageFault objeto que descreve a falha. Outras sobrecargas são fornecidas para conveniência. A primeira dessas sobrecargas pega uma FaultCode
cadeia de caracteres e uma razão e cria um MessageFault
uso MessageFault.CreateFault
usando essas informações. A outra sobrecarga pega um objeto de detalhe e também o passa para CreateFault
junto com o código de falha e o motivo. Por exemplo, a operação a seguir retorna uma falha.
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
Extraindo dados do corpo da mensagem
A Message
classe suporta várias maneiras de extrair informações de seu corpo. Estes podem ser classificados nas seguintes categorias:
Fazer com que todo o corpo da mensagem seja escrito de uma só vez para um gravador XML. Isso é conhecido como escrever uma mensagem.
Obter um leitor XML sobre o corpo da mensagem. Isso permite que você acesse posteriormente o corpo da mensagem peça por peça, conforme necessário. Isso é conhecido como leitura de uma mensagem.
A mensagem inteira, incluindo seu corpo, pode ser copiada para um buffer na memória do MessageBuffer tipo. Isso é conhecido como copiar uma mensagem.
Você pode acessar o corpo de um apenas uma Message
vez, independentemente de como ele é acessado. Um objeto message tem uma State
propriedade, que é inicialmente definida como Created. Os três métodos de acesso descritos na lista anterior definem o estado como Escrito, Leitura e Copiado, respectivamente. Além disso, um Close
método pode definir o estado como Fechado quando o conteúdo do corpo da mensagem não é mais necessário. O corpo da mensagem pode ser acessado somente no estado Criado, e não há como voltar ao estado Criado depois que o estado for alterado.
Escrevendo mensagens
O WriteBodyContents(XmlDictionaryWriter) método grava o conteúdo do corpo de uma determinada Message
instância para um determinado gravador XML. O WriteBody método faz o mesmo, exceto que ele inclui o conteúdo do corpo no elemento invólucro apropriado (por exemplo, <soap:body>
). Finalmente, WriteMessage escreve a mensagem inteira, incluindo o envelope SOAP de encapsulamento e os cabeçalhos. Se SOAP estiver desativado (Version is MessageVersion.None), todos os três métodos fazem a mesma coisa: eles escrevem o conteúdo do corpo da mensagem.
Por exemplo, o código a seguir grava o corpo de uma mensagem de entrada em um arquivo.
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
Dois métodos auxiliares adicionais gravam determinadas tags de elemento de início SOAP. Esses métodos não acessam o corpo da mensagem e, portanto, não alteram o estado da mensagem. Estes são, entre outros:
WriteStartBody escreve o elemento start body, por exemplo,
<soap:Body>
.WriteStartEnvelope Grava o elemento Start Envelope, por exemplo,
<soap:Envelope>
.
Para escrever as tags de elemento final correspondentes, chame WriteEndElement
o gravador XML correspondente. Esses métodos raramente são chamados diretamente.
Lendo mensagens
A principal maneira de ler um corpo de mensagem é chamar GetReaderAtBodyContents. Você recebe de volta um XmlDictionaryReader que você pode usar para ler o corpo da mensagem. Observe que as Message transições para o estado de leitura assim GetReaderAtBodyContents que é chamado, e não quando você usa o leitor XML retornado.
O GetBody método também permite que você acesse o corpo da mensagem como um objeto digitado. Internamente, esse método usa GetReaderAtBodyContents
o , e por isso também faz a transição do estado da mensagem para o Read estado (consulte a State propriedade).
É uma boa prática verificar a IsEmpty propriedade, caso em que o corpo da mensagem está vazio e GetReaderAtBodyContents lança um InvalidOperationExceptionarquivo . Além disso, se for uma mensagem recebida (por exemplo, a resposta), você também pode querer verificar IsFault, o que indica se a mensagem contém uma falha.
A sobrecarga mais básica de desserializa o corpo da GetBody mensagem em uma instância de um tipo (indicada pelo parâmetro genérico) usando um DataContractSerializer configurado com as configurações padrão e com a cota MaxItemsInObjectGraph desabilitada. Se você quiser usar um mecanismo de serialização diferente ou configurar o DataContractSerializer
de forma não padrão, use a GetBody sobrecarga que leva um XmlObjectSerializerarquivo .
Por exemplo, o código a seguir extrai dados de um corpo de mensagem que contém um objeto serializado Person
e imprime o nome da pessoa.
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
Copiando uma mensagem para um buffer
Às vezes, é necessário acessar o corpo da mensagem mais de uma vez, por exemplo, para encaminhar a mesma mensagem para vários destinos como parte de um sistema editor-assinante. Neste caso, é necessário armazenar em buffer toda a mensagem (incluindo o corpo) na memória. Você pode fazer isso ligando para CreateBufferedCopy(Int32). Esse método usa um parâmetro inteiro que representa o tamanho máximo do buffer e cria um buffer não maior do que esse tamanho. É importante definir isso como um valor seguro se a mensagem for proveniente de uma fonte não confiável.
O buffer é retornado como uma MessageBuffer instância. Você pode acessar dados no buffer de várias maneiras. A principal maneira é chamar CreateMessage para criar Message
instâncias a partir do buffer.
Outra maneira de acessar os dados no buffer é implementar a IXPathNavigable interface que a MessageBuffer classe implementa para acessar o XML subjacente diretamente. Algumas CreateNavigator sobrecargas permitem criar System.Xml.XPath navegadores protegidos por uma cota de nó, limitando o número de nós XML que podem ser visitados. Isso ajuda a evitar ataques de negação de serviço com base no longo tempo de processamento. Esta cotação está desativada por padrão. Algumas CreateNavigator
sobrecargas permitem especificar como o espaço em branco deve ser tratado no XML usando a XmlSpace enumeração, com o padrão sendo XmlSpace.None
.
Uma última maneira de acessar o conteúdo de um buffer de mensagens é gravar seu conteúdo em um fluxo usando WriteMessageo .
O exemplo a seguir demonstra o processo de trabalhar com um MessageBuffer
: uma mensagem de entrada é encaminhada para vários destinatários e, em seguida, registrada em um arquivo. Sem buffer, isso não é possível, porque o corpo da mensagem pode ser acessado apenas uma 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
A MessageBuffer
classe tem outros membros dignos de nota. O Close método pode ser chamado para liberar recursos quando o conteúdo do buffer não é mais necessário. A BufferSize propriedade retorna o tamanho do buffer alocado. A MessageContentType propriedade retorna o tipo de conteúdo MIME da mensagem.
Acessando o corpo da mensagem para depuração
Para fins de depuração, você pode chamar o ToString método para obter uma representação da mensagem como uma cadeia de caracteres. Essa representação geralmente corresponde à aparência de uma mensagem se fosse codificada com o codificador de texto, exceto que o XML seria melhor formatado para legibilidade humana. A única exceção é o corpo da mensagem. O corpo pode ser lido apenas uma vez e ToString
não altera o estado da mensagem. Portanto, o ToString
método pode não ser capaz de acessar o corpo e pode substituir um espaço reservado (por exemplo, "..." ou três pontos) em vez do corpo da mensagem. Portanto, não use ToString
para registrar mensagens se o conteúdo do corpo das mensagens for importante.
Acessando outras partes da mensagem
Várias propriedades são fornecidas para acessar informações sobre a mensagem além de seu conteúdo corporal. No entanto, estes não podem ser chamados depois de a mensagem ter sido fechada:
A Headers propriedade representa os cabeçalhos das mensagens. Consulte a seção "Trabalhando com cabeçalhos" mais adiante neste tópico.
A Properties propriedade representa as propriedades da mensagem, que são partes de dados nomeados anexados à mensagem que geralmente não são emitidos quando a mensagem é enviada. Consulte a seção "Trabalhando com propriedades" mais adiante neste tópico.
A Version propriedade indica a versão SOAP e WS-Addressing associada à mensagem ou
None
se SOAP está desabilitado.A IsFault propriedade retorna
true
se a mensagem for uma mensagem de falha SOAP.A IsEmpty propriedade retorna
true
se a mensagem estiver vazia.
Você pode usar o GetBodyAttribute(String, String) método para acessar um atributo específico no elemento de wrapper de corpo (por exemplo, <soap:Body>
) identificado por um nome e namespace específicos. Se tal atributo não for encontrado, null
será retornado. Este método pode ser chamado somente quando o Message
está no estado criado (quando o corpo da mensagem ainda não foi acessado).
Trabalhando com cabeçalhos
A Message
pode conter qualquer número de fragmentos XML nomeados, chamados cabeçalhos. Cada fragmento normalmente é mapeado para um cabeçalho SOAP. Os cabeçalhos são acessados através da Headers
propriedade do tipo MessageHeaders. MessageHeadersé uma coleção de objetos, e cabeçalhos individuais podem ser acessados através de sua IEnumerable interface ou através de MessageHeaderInfo seu indexador. Por exemplo, o código a seguir lista os nomes de todos os cabeçalhos em um Message
arquivo .
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
Adicionar, remover, localizar cabeçalhos
Você pode adicionar um novo cabeçalho no final de todos os cabeçalhos existentes usando o Add método. Você pode usar o Insert método para inserir um cabeçalho em um determinado índice. Os cabeçalhos existentes são deslocados para o item inserido. Os cabeçalhos são ordenados de acordo com seu índice, e o primeiro índice disponível é 0. Você pode usar as várias CopyHeadersFrom sobrecargas de método para adicionar cabeçalhos de uma instância ou MessageHeaders
diferenteMessage
. Algumas sobrecargas copiam um cabeçalho individual, enquanto outras copiam todos eles. O Clear método remove todos os cabeçalhos. O RemoveAt método remove um cabeçalho em um determinado índice (deslocando todos os cabeçalhos depois dele). O RemoveAll método remove todos os cabeçalhos com um nome e namespace específicos.
Recupere um cabeçalho específico usando o FindHeader método. Esse método usa o nome e o namespace do cabeçalho para localizar e retorna seu índice. Se o cabeçalho ocorrer mais de uma vez, uma exceção será lançada. Se o cabeçalho não for encontrado, ele retornará -1.
No modelo de cabeçalho SOAP, os cabeçalhos podem ter um Actor
valor que especifica o destinatário pretendido do cabeçalho. A sobrecarga mais básica FindHeader
pesquisa apenas cabeçalhos destinados ao recetor final da mensagem. No entanto, outra sobrecarga permite especificar quais Actor
valores são incluídos na pesquisa. Para obter mais informações, consulte a especificação SOAP.
Um CopyTo(MessageHeaderInfo[], Int32) método é fornecido para copiar cabeçalhos de uma MessageHeaders coleção para uma matriz de MessageHeaderInfo objetos.
Para acessar os dados XML em um cabeçalho, você pode chamar GetReaderAtHeader e retornar um leitor XML para o índice de cabeçalho específico. Se você quiser desserializar o conteúdo do cabeçalho em um objeto, use GetHeader<T>(Int32) ou uma das outras sobrecargas. As sobrecargas mais básicas desserializam cabeçalhos usando o DataContractSerializer configurado da maneira padrão. Se você quiser usar um serializador diferente ou uma configuração diferente do DataContractSerializer
, use uma das sobrecargas que levam um XmlObjectSerializer
arquivo . Há também sobrecargas que tomam o nome do cabeçalho, namespace e, opcionalmente, uma lista de valores em vez de Actor
um índice, esta é uma combinação de FindHeader
e GetHeader
.
Trabalhando com propriedades
Uma Message
instância pode conter um número arbitrário de objetos nomeados de tipos arbitrários. Esta coleção é acessada através da Properties
propriedade do tipo MessageProperties
. A coleção implementa a IDictionary<TKey,TValue> interface e atua como um mapeamento de String para Object. Normalmente, os valores de propriedade não são mapeados diretamente para qualquer parte da mensagem no fio, mas fornecem várias dicas de processamento de mensagens para os vários canais na pilha de canais WCF ou para a CopyTo(MessageHeaderInfo[], Int32) estrutura de serviço. Para obter um exemplo, consulte Visão geral da arquitetura de transferência de dados.
Herdando da classe Message
Se os tipos de mensagem internos criados usando CreateMessage
não atenderem aos seus requisitos, crie uma classe que derive da Message
classe.
Definindo o conteúdo do corpo da mensagem
Existem três técnicas principais para acessar dados dentro de um corpo de mensagem: escrever, ler e copiá-los para um buffer. Em última análise, essas operações resultam no OnWriteBodyContents, OnGetReaderAtBodyContentse OnCreateBufferedCopy métodos sendo chamados, respectivamente, em sua classe derivada de Message
. A classe base Message
garante que apenas um desses métodos seja chamado para cada Message
instância e que não seja chamado mais de uma vez. A classe base também garante que os métodos não sejam chamados em uma mensagem fechada. Não há necessidade de controlar o estado da mensagem em sua implementação.
OnWriteBodyContents é um método abstrato e deve ser implementado. A maneira mais básica de definir o conteúdo do corpo da sua mensagem é escrever usando este método. Por exemplo, a mensagem a seguir contém 100.000 números aleatórios 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.
Os OnGetReaderAtBodyContents() métodos e OnCreateBufferedCopy têm implementações padrão que funcionam para a maioria dos casos. As implementações padrão chamam OnWriteBodyContents, armazenam em buffer os resultados e trabalham com o buffer resultante. No entanto, em alguns casos, isso pode não ser suficiente. No exemplo anterior, a leitura da mensagem resulta em 100.000 elementos XML sendo armazenados em buffer, o que pode não ser desejável. Talvez você queira substituir OnGetReaderAtBodyContents() para retornar uma classe derivada personalizada XmlDictionaryReader que serve números aleatórios. Em seguida, você pode substituir OnWriteBodyContents para usar o leitor que o OnGetReaderAtBodyContents() método retorna, conforme mostrado no exemplo a seguir.
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
Da mesma forma, você pode querer substituir OnCreateBufferedCopy
para retornar sua própria MessageBuffer
classe derivada.
Além de fornecer o conteúdo do corpo da mensagem, sua classe derivada da mensagem também deve substituir as Version
propriedades , Headers
e .Properties
Observe que, se você criar uma cópia de uma mensagem, a cópia usará os cabeçalhos da mensagem do original.
Outros membros que podem ser substituídos
Você pode substituir os OnWriteStartEnvelopemétodos , OnWriteStartHeaderse para OnWriteStartBody especificar como as marcas de início do envelope SOAP, cabeçalhos SOAP e elemento de início do elemento corpo SOAP são gravadas. Estes correspondem normalmente a <soap:Envelope>
, <soap:Header>
e <soap:Body>
. Esses métodos normalmente não devem gravar nada se a Version propriedade retornar None.
Nota
A implementação padrão de chamadas OnWriteStartEnvelope
e OnWriteStartBody
antes de OnGetReaderAtBodyContents
chamar OnWriteBodyContents
e armazenar em buffer os resultados. Os cabeçalhos não são escritos.
Substitua o OnWriteMessage método para alterar a maneira como toda a mensagem é construída a partir de suas várias partes. O OnWriteMessage
método é chamado de WriteMessage e da implementação padrão OnCreateBufferedCopy . Observe que a substituição WriteMessage não é uma prática recomendada. É melhor substituir os métodos apropriados On
(por exemplo, OnWriteStartEnvelope, OnWriteStartHeaders, e OnWriteBodyContents.
Substituir OnBodyToString para substituir como o corpo da mensagem é representado durante a depuração. O padrão é representá-lo como três pontos ("..."). Observe que esse método pode ser chamado várias vezes quando o estado da mensagem é qualquer coisa diferente de Fechado. Uma implementação desse método nunca deve causar qualquer ação que deve ser executada apenas uma vez (como a leitura de um fluxo somente para frente).
Substitua o OnGetBodyAttribute método para permitir o acesso a atributos no elemento body SOAP. Esse método pode ser chamado qualquer número de vezes, mas o Message
tipo base garante que ele só é chamado quando a mensagem está no estado Criado. Não é necessário verificar o estado em uma implementação. A implementação padrão sempre retorna null
, o que indica que não há atributos no elemento body.
Se o objeto Message
precisar fazer qualquer limpeza especial quando o corpo da mensagem não for mais necessário, você poderá substituir OnCloseo . A implementação padrão não faz nada.
As IsEmpty
propriedades e IsFault
podem ser substituídas. Por padrão, ambos retornam false
.