Partilhar via


Usando contratos de mensagem

Normalmente, ao criar aplicativos do Windows Communication Foundation (WCF), os desenvolvedores prestam muita atenção às estruturas de dados e problemas de serialização e não precisam se preocupar com a estrutura das mensagens nas quais os dados são transportados. Para esses aplicativos, a criação de contratos de dados para os parâmetros ou valores de retorno é simples. (Para obter mais informações, consulte Especificando a transferência de dados em contratos de serviços.)

No entanto, às vezes, o controle completo sobre a estrutura de uma mensagem SOAP é tão importante quanto o controle sobre seu conteúdo. Isto é especialmente verdadeiro quando a interoperabilidade é importante ou para controlar especificamente questões de segurança ao nível da mensagem ou parte da mensagem. Nesses casos, você pode criar um contrato de mensagem que permite especificar a estrutura da mensagem SOAP precisa necessária.

Este tópico discute como usar os vários atributos de contrato de mensagem para criar um contrato de mensagem específico para sua operação.

Usando contratos de mensagem em operações

O WCF oferece suporte a operações modeladas no estilo RPC (chamada de procedimento remoto) ou no estilo de mensagens. Em uma operação no estilo RPC, você pode usar qualquer tipo serializável e tem acesso aos recursos disponíveis para chamadas locais, como vários parâmetros e refout parâmetros. Nesse estilo, a forma de serialização escolhida controla a estrutura dos dados nas mensagens subjacentes, e o tempo de execução do WCF cria as mensagens para dar suporte à operação. Isso permite que os desenvolvedores que não estão familiarizados com mensagens SOAP e SOAP criem e usem aplicativos de serviço de forma rápida e fácil.

O exemplo de código a seguir mostra uma operação de serviço modelada no estilo RPC.

[OperationContract]  
public BankingTransactionResponse PostBankingTransaction(BankingTransaction bt);  

Normalmente, um contrato de dados é suficiente para definir o esquema para as mensagens. Por exemplo, no exemplo anterior, é suficiente para a maioria dos aplicativos se e BankingTransactionResponse têm contratos de dados para BankingTransaction definir o conteúdo das mensagens SOAP subjacentes. Para obter mais informações sobre contratos de dados, consulte Usando contratos de dados.

No entanto, ocasionalmente é necessário controlar com precisão como a estrutura da mensagem SOAP transmitida através do fio. O cenário mais comum para isso é inserir cabeçalhos SOAP personalizados. Outro cenário comum é definir propriedades de segurança para os cabeçalhos e o corpo da mensagem, ou seja, decidir se esses elementos são assinados digitalmente e criptografados. Finalmente, algumas pilhas SOAP de terceiros exigem que as mensagens estejam em um formato específico. As operações no estilo de mensagens fornecem esse controle.

Uma operação de estilo de mensagem tem no máximo um parâmetro e um valor de retorno onde ambos os tipos são tipos de mensagem; ou seja, eles serializam diretamente em uma estrutura de mensagem SOAP especificada. Pode ser qualquer tipo marcado com o MessageContractAttribute ou o Message tipo. O exemplo de código a seguir mostra uma operação semelhante ao estilo RCP anterior, mas que usa o estilo de mensagens.

Por exemplo, se BankingTransaction e BankingTransactionResponse são ambos os tipos que são contratos de mensagem, então o código nas operações a seguir é válido.

[OperationContract]  
BankingTransactionResponse Process(BankingTransaction bt);  
[OperationContract]  
void Store(BankingTransaction bt);  
[OperationContract]  
BankingTransactionResponse GetResponse();  

No entanto, o código a seguir é inválido.

[OperationContract]  
bool Validate(BankingTransaction bt);  
// Invalid, the return type is not a message contract.  
[OperationContract]  
void Reconcile(BankingTransaction bt1, BankingTransaction bt2);  
// Invalid, there is more than one parameter.  

Uma exceção é lançada para qualquer operação que envolva um tipo de contrato de mensagem e que não siga um dos padrões válidos. É claro que as operações que não envolvem tipos de contrato de mensagem não estão sujeitas a essas restrições.

Se um tipo tiver um contrato de mensagem e um contrato de dados, somente seu contrato de mensagem será considerado quando o tipo for usado em uma operação.

Definição de contratos de mensagem

Para definir um contrato de mensagem para um tipo (ou seja, para definir o mapeamento entre o tipo e um envelope SOAP), aplique o MessageContractAttribute ao tipo. Em seguida, aplique o MessageHeaderAttribute aos membros do tipo que você deseja transformar em cabeçalhos SOAP e aplique o MessageBodyMemberAttribute aos membros que você deseja transformar em partes do corpo SOAP da mensagem.

O código a seguir fornece um exemplo de uso de um contrato de mensagem.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageHeader] public DateTime transactionDate;  
  [MessageBodyMember] private Account sourceAccount;  
  [MessageBodyMember] private Account targetAccount;  
  [MessageBodyMember] public int amount;  
}  

Ao usar esse tipo como um parâmetro de operação, o seguinte envelope SOAP é gerado:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">  
  <s:Header>  
    <h:operation xmlns:h="http://tempuri.org/" xmlns="http://tempuri.org/">Deposit</h:operation>  
    <h:transactionDate xmlns:h="http://tempuri.org/" xmlns="http://tempuri.org/">2012-02-16T16:10:00</h:transactionDate>  
  </s:Header>  
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
    <BankingTransaction xmlns="http://tempuri.org/">  
      <amount>0</amount>  
      <sourceAccount xsi:nil="true"/>  
      <targetAccount xsi:nil="true"/>  
    </BankingTransaction>  
  </s:Body>  
</s:Envelope>  

Observe que operation e transactionDate aparecem como cabeçalhos SOAP e o corpo SOAP consiste em um elemento BankingTransaction wrapper contendo sourceAccount,targetAccount, e amount.

Você pode aplicar o MessageHeaderAttribute e MessageBodyMemberAttribute a todos os campos, propriedades e eventos, independentemente de serem públicos, privados, protegidos ou internos.

O MessageContractAttribute permite que você especifique os atributos WrapperName e WrapperNamespace que controlam o nome do elemento wrapper no corpo da mensagem SOAP. Por padrão, o nome do tipo de contrato de mensagem é usado para o wrapper e o namespace no qual o contrato de mensagem é definido http://tempuri.org/ é usado como o namespace padrão.

Nota

KnownTypeAttribute atributos são ignorados em contratos de mensagem. Se for KnownTypeAttribute necessário, coloque-o na operação que está usando o contrato de mensagem em questão.

Controlando nomes e namespaces de cabeçalhos e partes do corpo

Na representação SOAP de um contrato de mensagem, cada cabeçalho e parte do corpo é mapeado para um elemento XML que tem um nome e um namespace.

Por padrão, o namespace é o mesmo que o namespace do contrato de serviço do qual a mensagem está participando, e o nome é determinado pelo nome do membro ao qual os atributos ou são MessageHeaderAttributeMessageBodyMemberAttribute aplicados.

Você pode alterar esses padrões manipulando o MessageContractMemberAttribute.Name e MessageContractMemberAttribute.Namespace (na classe pai dos MessageHeaderAttribute atributos e MessageBodyMemberAttribute ).

Considere a classe no exemplo de código a seguir.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageHeader(Namespace="http://schemas.contoso.com/auditing/2005")] public bool IsAudited;  
  [MessageBodyMember(Name="transactionData")] public BankingTransactionData theData;  
}  

Neste exemplo, o IsAudited cabeçalho está no namespace especificado no código e a parte do corpo que representa o theData membro é representada por um elemento XML com o nome transactionData. A seguir mostra o XML gerado para este contrato de mensagem.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">  
  <s:Header>  
    <h:IsAudited xmlns:h="http://schemas.contoso.com/auditing/2005" xmlns="http://schemas.contoso.com/auditing/2005">false</h:IsAudited>  
    <h:operation xmlns:h="http://tempuri.org/" xmlns="http://tempuri.org/">Deposit</h:operation>  
  </s:Header>  
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
    <AuditedBankingTransaction xmlns="http://tempuri.org/">  
      <transactionData/>  
    </AuditedBankingTransaction>  
  </s:Body>  
</s:Envelope>  

Controlando se as partes do corpo do SOAP estão envolvidas

Por padrão, as partes do corpo SOAP são serializadas dentro de um elemento encapsulado. Por exemplo, o código a seguir mostra o HelloGreetingMessage elemento wrapper gerado a partir do nome do MessageContractAttribute tipo no contrato de mensagem para a HelloGreetingMessage mensagem.

[MessageContract]
public class HelloGreetingMessage
{
  private string localGreeting;

  [MessageBodyMember(
    Name = "Salutations",
    Namespace = "http://www.examples.com"
  )]
  public string Greeting
  {
    get { return localGreeting; }
    set { localGreeting = value; }
  }
}

/*
 The following is the request message, edited for clarity.

  <s:Envelope>
    <s:Header>
      <!-- Note: Some header content has been removed for clarity.
      <a:Action>http://GreetingMessage/Action</a:Action>
      <a:To s:mustUnderstand="1"></a:To>
    </s:Header>
    <s:Body u:Id="_0" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
      <HelloGreetingMessage xmlns="Microsoft.WCF.Documentation">
        <Salutations xmlns="http://www.examples.com">Hello.</Salutations>
      </HelloGreetingMessage>
    </s:Body>
 </s:Envelope>
 */
<MessageContract> _
Public Class HelloGreetingMessage
    Private localGreeting As String

    <MessageBodyMember(Name:="Salutations", Namespace:="http://www.examples.com")> _
    Public Property Greeting() As String
        Get
            Return localGreeting
        End Get
        Set(ByVal value As String)
            localGreeting = value
        End Set
    End Property
End Class

'  
'   The following is the request message, edited for clarity.
'    
'    <s:Envelope>
'      <s:Header>
'        <!-- Note: Some header content has been removed for clarity.
'        <a:Action>http://GreetingMessage/Action</a:Action> 
'        <a:To s:mustUnderstand="1"></a:To>
'      </s:Header>
'      <s:Body u:Id="_0" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
'        <HelloGreetingMessage xmlns="Microsoft.WCF.Documentation">
'          <Salutations xmlns="http://www.examples.com">Hello.</Salutations>
'      </s:Body>
'   </s:Envelope>
'   

Para suprimir o elemento wrapper, defina a IsWrapped propriedade como false. Para controlar o nome e o namespace do elemento wrapper, use as WrapperName propriedades e WrapperNamespace .

Nota

Ter mais de uma parte do corpo da mensagem em mensagens que não são encapsuladas não é compatível com o WS-I Basic Profile 1.1 e não é recomendado ao projetar novos contratos de mensagem. No entanto, pode ser necessário ter mais de uma parte do corpo da mensagem desempacotada em determinados cenários específicos de interoperabilidade. Se você vai transmitir mais de um dado em um corpo de mensagem, é recomendável usar o modo padrão (encapsulado). Ter mais de um cabeçalho de mensagem em mensagens desempacotadas é completamente aceitável.

Usando tipos personalizados dentro de contratos de mensagem

Cada cabeçalho de mensagem individual e parte do corpo da mensagem é serializado (transformado em XML) usando o mecanismo de serialização escolhido para o contrato de serviço onde a mensagem é usada. O mecanismo de serialização padrão, o XmlFormatter, pode lidar com qualquer tipo que tenha um contrato de dados, seja explicitamente (por ter o System.Runtime.Serialization.DataContractAttribute) ou implicitamente (por ser um tipo primitivo, ter o System.SerializableAttribute, e assim por diante). Para obter mais informações, consulte Usando contratos de dados.

No exemplo anterior, os Operation tipos e BankingTransactionData devem ter um contrato de dados e transactionDate são serializáveis porque DateTime são primitivos (e, portanto, têm um contrato de dados implícito).

No entanto, é possível alternar para um mecanismo de serialização diferente, o XmlSerializer. Se você fizer essa opção, deverá garantir que todos os tipos usados para cabeçalhos de mensagens e partes do corpo sejam serializáveis usando o XmlSerializer.

Usando matrizes dentro de contratos de mensagem

Você pode usar matrizes de elementos repetitivos em contratos de mensagem de duas maneiras.

A primeira é usar um MessageHeaderAttribute ou um MessageBodyMemberAttribute diretamente na matriz. Nesse caso, toda a matriz é serializada como um elemento (ou seja, um cabeçalho ou uma parte do corpo) com vários elementos filho. Considere a classe no exemplo a seguir.

[MessageContract]  
public class BankingDepositLog  
{  
  [MessageHeader] public int numRecords;  
  [MessageHeader] public DepositRecord[] records;  
  [MessageHeader] public int branchID;  
}  

Isso resulta em cabeçalhos SOAP é semelhante ao seguinte.

<BankingDepositLog>  
<numRecords>3</numRecords>  
<records>  
  <DepositRecord>Record1</DepositRecord>  
  <DepositRecord>Record2</DepositRecord>  
  <DepositRecord>Record3</DepositRecord>  
</records>  
<branchID>20643</branchID>  
</BankingDepositLog>  

Uma alternativa a isso é usar o MessageHeaderArrayAttribute. Nesse caso, cada elemento de matriz é serializado independentemente e de modo que cada elemento de matriz tenha um cabeçalho, semelhante ao seguinte.

<numRecords>3</numRecords>  
<records>Record1</records>  
<records>Record2</records>  
<records>Record3</records>  
<branchID>20643</branchID>  

O nome padrão para entradas de matriz é o nome do membro ao qual os MessageHeaderArrayAttribute atributos são aplicados.

O MessageHeaderArrayAttribute atributo herda do MessageHeaderAttribute. Ele tem o mesmo conjunto de recursos que os atributos não-matriz, por exemplo, é possível definir a ordem, nome e namespace para uma matriz de cabeçalhos da mesma forma que você defini-lo para um único cabeçalho. Quando você usa a Order propriedade em uma matriz, ela se aplica a toda a matriz.

Você pode aplicar o MessageHeaderArrayAttribute somente a matrizes, não a coleções.

Usando matrizes de bytes em contratos de mensagem

Matrizes de bytes, quando usadas com os atributos não-matriz (MessageBodyMemberAttribute e MessageHeaderAttribute), não são tratadas como matrizes, mas como um tipo primitivo especial representado como dados codificados em Base64 no XML resultante.

Quando você usa matrizes de bytes com o atributo MessageHeaderArrayAttributearray , os resultados dependem do serializador em uso. Com o serializador padrão, a matriz é representada como uma entrada individual para cada byte. No entanto, quando o XmlSerializer é selecionado (usando o XmlSerializerFormatAttribute no contrato de serviço), as matrizes de bytes são tratadas como dados Base64, independentemente de os atributos de matriz ou não-matriz serem usados.

Assinando e criptografando partes da mensagem

Um contrato de mensagem pode indicar se os cabeçalhos e/ou o corpo da mensagem devem ser assinados digitalmente e criptografados.

Isso é feito definindo a MessageContractMemberAttribute.ProtectionLevel propriedade nos MessageHeaderAttribute atributos e MessageBodyMemberAttribute . A propriedade é uma enumeração do System.Net.Security.ProtectionLevel tipo e pode ser definida como None (sem criptografia ou assinatura), Sign (somente assinatura digital) ou EncryptAndSign (criptografia e assinatura digital). A predefinição é EncryptAndSign.

Para que esses recursos de segurança funcionem, você deve configurar corretamente a associação e os comportamentos. Se você usar esses recursos de segurança sem a configuração adequada (por exemplo, tentar assinar uma mensagem sem fornecer suas credenciais), uma exceção será lançada no momento da validação.

Para cabeçalhos de mensagens, o nível de proteção é determinado individualmente para cada cabeçalho.

Para partes do corpo da mensagem, o nível de proteção pode ser considerado como o "nível mínimo de proteção". O corpo tem apenas um nível de proteção, independentemente do número de partes do corpo. O nível de proteção do corpo é determinado pela configuração de propriedade mais alta ProtectionLevel de todas as partes do corpo. No entanto, deve definir o nível de proteção de cada parte do corpo para o nível mínimo de proteção real exigido.

Considere a classe no exemplo de código a seguir.

[MessageContract]  
public class PatientRecord  
{  
   [MessageHeader(ProtectionLevel=None)] public int recordID;  
   [MessageHeader(ProtectionLevel=Sign)] public string patientName;  
   [MessageHeader(ProtectionLevel=EncryptAndSign)] public string SSN;  
   [MessageBodyMember(ProtectionLevel=None)] public string comments;  
   [MessageBodyMember(ProtectionLevel=Sign)] public string diagnosis;  
   [MessageBodyMember(ProtectionLevel=EncryptAndSign)] public string medicalHistory;  
}  

Neste exemplo, o recordID cabeçalho não está protegido, patientName é signed, e SSN é criptografado e assinado. Pelo menos uma parte do corpo, medicalHistory, foi EncryptAndSign aplicada e, portanto, todo o corpo da mensagem é criptografado e assinado, embora os comentários e as partes do corpo do diagnóstico especifiquem níveis de proteção mais baixos.

Ação SOAP

SOAP e padrões de serviços da Web relacionados definem uma propriedade chamada Action que pode estar presente para cada mensagem SOAP enviada. A operação OperationContractAttribute.Action e OperationContractAttribute.ReplyAction as propriedades controlam o valor dessa propriedade.

Atributos de cabeçalho SOAP

O padrão SOAP define os seguintes atributos que podem existir em um cabeçalho:

  • Actor/RoleActor( em SOAP 1.1, Role em SOAP 1.2)

  • MustUnderstand

  • Relay

O Actor atributo or Role especifica o URI (Uniform Resource Identifier) do nó ao qual um determinado cabeçalho se destina. O MustUnderstand atributo especifica se o nó que processa o cabeçalho deve compreendê-lo. O Relay atributo especifica se o cabeçalho deve ser retransmitido para nós downstream. WCF não executa qualquer processamento desses atributos em mensagens de entrada, exceto para o MustUnderstand atributo, conforme especificado na seção "Controle de versão de contrato de mensagem" mais adiante neste tópico. No entanto, ele permite que você leia e escreva esses atributos conforme necessário, como na descrição a seguir.

Ao enviar uma mensagem, esses atributos não são emitidos por padrão. Você pode alterar isso de duas maneiras. Primeiro, você pode definir estaticamente os atributos para quaisquer valores desejados alterando as MessageHeaderAttribute.Actorpropriedades , MessageHeaderAttribute.MustUnderstande MessageHeaderAttribute.Relay , conforme mostrado no exemplo de código a seguir. (Observe que não há nenhuma Role propriedade; definir a Actor propriedade emite o Role atributo se você estiver usando SOAP 1.2).

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader(Actor="http://auditingservice.contoso.com", MustUnderstand=true)] public bool IsAudited;  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember] public BankingTransactionData theData;  
}  

A segunda maneira de controlar esses atributos é dinamicamente, através do código. Você pode conseguir isso encapsulando o tipo de cabeçalho desejado no MessageHeader<T> tipo (certifique-se de não confundir esse tipo com a versão não genérica) e usando o tipo junto com o MessageHeaderAttribute. Em seguida, você pode usar propriedades no MessageHeader<T> para definir os atributos SOAP, conforme mostrado no exemplo de código a seguir.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public MessageHeader<bool> IsAudited;  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember] public BankingTransactionData theData;  
}  
// application code:  
BankingTransaction bt = new BankingTransaction();  
bt.IsAudited = new MessageHeader<bool>();  
bt.IsAudited.Content = false; // Set IsAudited header value to "false"  
bt.IsAudited.Actor="http://auditingservice.contoso.com";  
bt.IsAudited.MustUnderstand=true;  

Se você usar os mecanismos de controle dinâmico e estático, as configurações estáticas serão usadas como padrão, mas poderão ser substituídas posteriormente usando o mecanismo dinâmico, conforme mostrado no código a seguir.

[MessageHeader(MustUnderstand=true)] public MessageHeader<Person> documentApprover;  
// later on in the code:  
BankingTransaction bt = new BankingTransaction();  
bt.documentApprover = new MessageHeader<Person>();  
bt.documentApprover.MustUnderstand = false; // override the static default of 'true'  

A criação de cabeçalhos repetidos com controle de atributo dinâmico é permitida, conforme mostrado no código a seguir.

[MessageHeaderArray] public MessageHeader<Person> documentApprovers[];  

No lado recetor, a leitura desses atributos SOAP só pode ser feita se a MessageHeader<T> classe for usada para o cabeçalho no tipo. Examine as Actorpropriedades , Relayou MustUnderstand de um cabeçalho do MessageHeader<T> tipo para descobrir as configurações de atributo na mensagem recebida.

Quando uma mensagem é recebida e, em seguida, enviada de volta, as configurações do atributo SOAP só vão de ida e volta para cabeçalhos do MessageHeader<T> tipo.

Ordem das Partes do Corpo SOAP

Em algumas circunstâncias, pode ser necessário controlar a ordem das partes do corpo. A ordem dos elementos do corpo é alfabética por padrão, mas pode ser controlada pela MessageBodyMemberAttribute.Order propriedade. Essa propriedade tem a mesma semântica que a DataMemberAttribute.Order propriedade, exceto para o comportamento em cenários de herança (em contratos de mensagem, os membros do corpo do tipo base não são classificados antes dos membros do corpo do tipo derivado). Para obter mais informações, consulte Data Member Order.

No exemplo a seguir, amount normalmente viria primeiro porque é primeiro em ordem alfabética. No entanto, a Order propriedade coloca-o na terceira posição.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember(Order=1)] public Account sourceAccount;  
  [MessageBodyMember(Order=2)] public Account targetAccount;  
  [MessageBodyMember(Order=3)] public int amount;  
}  

Controle de versão de contrato de mensagem

Ocasionalmente, pode ser necessário alterar os contratos de mensagens. Por exemplo, uma nova versão do seu aplicativo pode adicionar um cabeçalho extra a uma mensagem. Então, ao enviar da nova versão para a antiga, o sistema deve lidar com um cabeçalho extra, bem como um cabeçalho ausente ao ir na outra direção.

As seguintes regras se aplicam aos cabeçalhos de controle de versão:

  • O WCF não se opõe aos cabeçalhos ausentes — os membros correspondentes são deixados em seus valores padrão.

  • O WCF também ignora cabeçalhos extras inesperados. A única exceção a essa regra é se o cabeçalho extra tiver um MustUnderstand atributo definido como true na mensagem SOAP de entrada — nesse caso, uma exceção é lançada porque um cabeçalho que deve ser entendido não pode ser processado.

Os corpos das mensagens têm regras de controle de versão semelhantes — as partes do corpo da mensagem ausentes e adicionais são ignoradas.

Considerações sobre herança

Um tipo de contrato de mensagem pode herdar de outro tipo, desde que o tipo base também tenha um contrato de mensagem.

Ao criar ou acessar uma mensagem usando um tipo de contrato de mensagem que herda de outros tipos de contrato de mensagem, as seguintes regras se aplicam:

  • Todos os cabeçalhos de mensagem na hierarquia de herança são coletados juntos para formar o conjunto completo de cabeçalhos para a mensagem.

  • Todas as partes do corpo da mensagem na hierarquia de herança são coletadas juntas para formar o corpo completo da mensagem. As partes do corpo são ordenadas de acordo com as regras habituais de ordenação (por MessageBodyMemberAttribute.Order propriedade e, em seguida, por ordem alfabética), sem relevância para o seu lugar na hierarquia de herança. O uso da herança de contrato de mensagem em que partes do corpo da mensagem ocorrem em vários níveis da árvore de herança é fortemente desencorajado. Se uma classe base e uma classe derivada definirem um cabeçalho ou uma parte do corpo com o mesmo nome, o membro da classe mais base será usado para armazenar o valor desse cabeçalho ou parte do corpo.

Considere as classes no exemplo de código a seguir.

[MessageContract]  
public class PersonRecord  
{  
  [MessageHeader(Name="ID")] public int personID;  
  [MessageBodyMember] public string patientName;  
}  
  
[MessageContract]  
public class PatientRecord : PersonRecord  
{  
  [MessageHeader(Name="ID")] public int patientID;  
  [MessageBodyMember] public string diagnosis;  
}  

A PatientRecord classe descreve uma mensagem com um cabeçalho chamado ID. O cabeçalho corresponde ao membro e não ao personIDpatientID membro, porque o membro mais base é escolhido. Assim, o patientID campo é inútil neste caso. O corpo da mensagem contém o diagnosis elemento seguido pelo patientName elemento , porque essa é a ordem alfabética. Observe que o exemplo mostra um padrão que é fortemente desencorajado: tanto os contratos de mensagem base quanto os derivados têm partes do corpo da mensagem.

Considerações sobre WSDL

Ao gerar um contrato WSDL (Web Services Description Language) a partir de um serviço que usa contratos de mensagem, é importante lembrar que nem todos os recursos do contrato de mensagem são refletidos no WSDL resultante. Considere os pontos seguintes:

  • WSDL não pode expressar o conceito de uma matriz de cabeçalhos. Ao criar mensagens com uma matriz de cabeçalhos usando o MessageHeaderArrayAttribute, o WSDL resultante reflete apenas um cabeçalho em vez da matriz.

  • O documento WSDL resultante pode não refletir algumas informações de nível de proteção.

  • O tipo de mensagem gerado no WSDL tem o mesmo nome que o nome da classe do tipo de contrato de mensagem.

  • Ao usar o mesmo contrato de mensagem em várias operações, vários tipos de mensagem são gerados no documento WSDL. Os nomes tornam-se únicos adicionando os números "2", "3", e assim por diante, para usos subsequentes. Ao importar de volta o WSDL, vários tipos de contrato de mensagem são criados e são idênticos, exceto por seus nomes.

Considerações sobre codificação SOAP

WCF permite que você use o estilo de codificação SOAP herdado de XML, no entanto, seu uso não é recomendado. Ao usar esse estilo (definindo a Use propriedade como Encoded no System.ServiceModel.XmlSerializerFormatAttribute aplicado ao contrato de serviço), as seguintes considerações adicionais se aplicam:

  • Os cabeçalhos das mensagens não são suportados; isso significa que o atributo MessageHeaderAttribute e o atributo MessageHeaderArrayAttribute array são incompatíveis com a codificação SOAP.

  • Se o contrato de mensagem não estiver encapsulado, ou seja, se a propriedade IsWrapped estiver definida como false, o contrato de mensagem poderá ter apenas uma parte do corpo.

  • O nome do elemento wrapper para o contrato de mensagem de solicitação deve corresponder ao nome da operação. Use a WrapperName propriedade do contrato de mensagem para isso.

  • O nome do elemento wrapper para o contrato de mensagem de resposta deve ser o mesmo que o nome da operação sufixada por 'Response'. Use a WrapperName propriedade do contrato de mensagem para isso.

  • A codificação SOAP preserva referências de objeto. Por exemplo, considere o código a seguir.

    [MessageContract(WrapperName="updateChangeRecord")]  
    public class ChangeRecordRequest  
    {  
      [MessageBodyMember] Person changedBy;  
      [MessageBodyMember] Person changedFrom;  
      [MessageBodyMember] Person changedTo;  
    }  
    
    [MessageContract(WrapperName="updateChangeRecordResponse")]  
    public class ChangeRecordResponse  
    {  
      [MessageBodyMember] Person changedBy;  
      [MessageBodyMember] Person changedFrom;  
      [MessageBodyMember] Person changedTo;  
    }  
    
    // application code:  
    ChangeRecordRequest cr = new ChangeRecordRequest();  
    Person p = new Person("John Doe");  
    cr.changedBy=p;  
    cr.changedFrom=p;  
    cr.changedTo=p;  
    

Depois de serializar a mensagem usando a codificação SOAP, e changedTo não contêm suas próprias cópias do p, mas em vez disso, changedFrom aponte para a cópia dentro do changedBy elemento .

Performance Considerations (Aplicações das Funções do Azure: Considerações de Desempenho)

Cada cabeçalho e parte do corpo da mensagem é serializado independentemente dos outros. Portanto, os mesmos namespaces podem ser declarados novamente para cada cabeçalho e parte do corpo. Para melhorar o desempenho, especialmente em termos do tamanho da mensagem no fio, consolide vários cabeçalhos e partes do corpo em um único cabeçalho ou parte do corpo. Por exemplo, em vez do seguinte código:

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember] public Account sourceAccount;  
  [MessageBodyMember] public Account targetAccount;  
  [MessageBodyMember] public int amount;  
}  

Use este código.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember] public OperationDetails details;  
}  
  
[DataContract]  
public class OperationDetails  
{  
  [DataMember] public Account sourceAccount;  
  [DataMember] public Account targetAccount;  
  [DataMember] public int amount;  
}  

Contratos assíncronos e de mensagem baseados em eventos

As diretrizes de design para o modelo assíncrono baseado em evento afirmam que, se mais de um valor for retornado, um valor será retornado como a Result propriedade e os outros serão retornados como propriedades no EventArgs objeto. Um resultado disso é que, se um cliente importar metadados usando as opções de comando assíncrono baseadas em evento e a operação retornar mais de um valor, o objeto padrão EventArgs retornará um valor como a Result propriedade e o restante serão propriedades do EventArgs objeto.

Se você quiser receber o objeto message como a Result propriedade e ter os valores retornados como propriedades nesse objeto, use a /messageContract opção de comando. Isso gera uma assinatura que retorna a mensagem de resposta como a Result propriedade no EventArgs objeto. Todos os valores de retorno internos são, então, propriedades do objeto de mensagem de resposta.

Consulte também