Expedição por elemento BodyDispatch by Body Element

Este exemplo demonstra como implementar um algoritmo alternativo para atribuir mensagens de entrada a operações.This sample demonstrates how to implement an alternate algorithm for assigning incoming messages to operations.

Por padrão, o Dispatcher do modelo de serviço seleciona o método de tratamento apropriado para uma mensagem de entrada com base no cabeçalho WS-Addressing "ação" da mensagem ou nas informações equivalentes na solicitação HTTP SOAP.By default, the service model dispatcher selects the appropriate handling method for an incoming message based on the message's WS-Addressing "Action" header or the equivalent information in the HTTP SOAP request.

Algumas pilhas de serviços Web SOAP 1,1 que não seguem as diretrizes WS-I Basic Profile 1,1 não expedem mensagens com base no URI de ação, mas com base no nome qualificado XML do primeiro elemento dentro do corpo SOAP.Some SOAP 1.1 Web services stacks that do not follow the WS-I Basic Profile 1.1 guidelines do not dispatch messages based on the Action URI, but rather based on the XML qualified name of the first element inside the SOAP body. Da mesma forma, o lado do cliente dessas pilhas pode enviar mensagens com um cabeçalho SOAPAction HTTP vazio ou arbitrário, que era permitido pela especificação SOAP 1,1.Likewise, the client side of these stacks might send messages with an empty or arbitrary HTTP SoapAction header, which was permitted by the SOAP 1.1 specification.

Para alterar a maneira como as mensagens são expedidas para métodos, o exemplo implementa a IDispatchOperationSelector interface de extensibilidade no DispatchByBodyElementOperationSelector .To change the way messages are dispatched to methods, the sample implements the IDispatchOperationSelector extensibility interface on the DispatchByBodyElementOperationSelector. Essa classe seleciona operações baseadas no primeiro elemento do corpo da mensagem.This class selects operations based on the first element of the message body.

O construtor de classe espera que um dicionário seja preenchido com pares de XmlQualifiedName cadeias de caracteres e, no qual os nomes qualificados indicam o nome do primeiro filho do corpo SOAP e as cadeias de caracteres indicam o nome da operação correspondente.The class constructor expects a dictionary populated with pairs of XmlQualifiedName and strings, whereby the qualified names indicate the name of the first child of the SOAP body and the strings indicate the matching operation name. O defaultOperationName é o nome da operação que recebe todas as mensagens que não podem ser correspondidas em relação a este dicionário:The defaultOperationName is the name of the operation that receives all messages that cannot be matched against this dictionary:

class DispatchByBodyElementOperationSelector : IDispatchOperationSelector  
{  
    Dictionary<XmlQualifiedName, string> dispatchDictionary;  
    string defaultOperationName;  
  
    public DispatchByBodyElementOperationSelector(Dictionary<XmlQualifiedName,string> dispatchDictionary, string defaultOperationName)  
    {  
        this.dispatchDictionary = dispatchDictionary;  
        this.defaultOperationName = defaultOperationName;  
    }  
}

IDispatchOperationSelector as implementações são muito simples de criar, pois há apenas um método na interface: SelectOperation .IDispatchOperationSelector implementations are very straightforward to build as there is only one method on the interface: SelectOperation. O trabalho desse método é inspecionar uma mensagem de entrada e retornar uma cadeia de caracteres que seja igual ao nome de um método no contrato de serviço para o ponto de extremidade atual.The job of this method is to inspect an incoming message and to return a string that equals the name of a method on the service contract for the current endpoint.

Neste exemplo, o seletor de operação adquire um XmlDictionaryReader para o corpo da mensagem de entrada usando GetReaderAtBodyContents .In this sample, the operation selector acquires an XmlDictionaryReader for the incoming message's body using GetReaderAtBodyContents. Esse método já posiciona o leitor no primeiro filho do corpo da mensagem para que seja suficiente obter o nome do elemento atual e o URI do namespace e combiná-los em um XmlQualifiedName que é usado para pesquisar a operação correspondente do dicionário mantido pelo seletor de operação.This method already positions the reader on the first child of the message's body so that it is sufficient to get the current element's name and namespace URI and combine them into an XmlQualifiedName that is then used for looking up the corresponding operation from the dictionary held by the operation selector.

public string SelectOperation(ref System.ServiceModel.Channels.Message message)  
{  
    XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();  
    XmlQualifiedName lookupQName = new  
       XmlQualifiedName(bodyReader.LocalName, bodyReader.NamespaceURI);  
    message = CreateMessageCopy(message,bodyReader);  
    if (dispatchDictionary.ContainsKey(lookupQName))  
    {  
         return dispatchDictionary[lookupQName];  
    }  
    else  
    {  
        return defaultOperationName;  
    }  
}  

Acessar o corpo da mensagem com GetReaderAtBodyContents ou qualquer um dos outros métodos que fornecem acesso ao conteúdo do corpo da mensagem faz com que a mensagem seja marcada como "leitura", o que significa que a mensagem é inválida para qualquer processamento adicional.Accessing the message body with GetReaderAtBodyContents or any of the other methods that provide access to the message's body content causes the message to be marked as "read", which means that the message is invalid for any further processing. Portanto, o seletor de operação cria uma cópia da mensagem de entrada com o método mostrado no código a seguir.Therefore, the operation selector creates a copy of the incoming message with the method shown in the following code. Como a posição do leitor não foi alterada durante a inspeção, ela pode ser referenciada pela mensagem recém-criada para a qual as propriedades de mensagem e os cabeçalhos de mensagem também são copiados, o que resulta em um clone exato da mensagem original:Because the reader's position has not been changed during the inspection, it can be referenced by the newly created message to which the message properties and the message headers are also copied, which results in an exact clone of the original message:

private Message CreateMessageCopy(Message message,
                                     XmlDictionaryReader body)  
{  
    Message copy = Message.CreateMessage(message.Version,message.Headers.Action,body);  
    copy.Headers.CopyHeaderFrom(message,0);  
    copy.Properties.CopyProperties(message.Properties);  
    return copy;  
}  

Adicionando um seletor de operação a um serviçoAdding an Operation Selector to a Service

Os seletores de operação de expedição de serviço são extensões para o Dispatcher do Windows Communication Foundation (WCF).Service dispatch operation selectors are extensions to the Windows Communication Foundation (WCF) dispatcher. Para selecionar métodos no canal de retorno de chamada de contratos duplex, também há seletores de operação do cliente, que funcionam muito como os seletores de operação de expedição descritos aqui, mas que não são explicitamente abordados neste exemplo.For selecting methods on the callback channel of duplex contracts, there are also client operation selectors, which work very much like the dispatch operation selectors described here, but which are not explicitly covered in this sample.

Assim como a maioria das extensões de modelo de serviço, os seletores de operação de expedição são adicionados ao dispatcher usando comportamentos.Like most service model extensions, dispatch operation selectors are added to the dispatcher using behaviors. Um comportamento é um objeto de configuração, que adiciona uma ou mais extensões ao tempo de execução de expedição (ou ao tempo de execução do cliente) ou altera suas configurações.A behavior is a configuration object, which either adds one or more extensions to the dispatch runtime (or to the client runtime) or otherwise changes its settings.

Como os seletores de operação têm o escopo do contrato, o comportamento apropriado a ser implementado aqui é o IContractBehavior .Because operation selectors have contract scope, the appropriate behavior to implement here is the IContractBehavior. Como a interface é implementada em uma Attribute classe derivada, conforme mostrado no código a seguir, o comportamento pode ser adicionado declarativamente a qualquer contrato de serviço.Because the interface is implemented on a Attribute derived class as shown in the following code, the behavior can be declaratively added to any service contract. Sempre que um ServiceHost é aberto e o tempo de execução de expedição é criado, todos os comportamentos encontrados como atributos em contratos, operações e implementações de serviço ou como elemento na configuração do serviço são adicionados automaticamente e, posteriormente, solicitados a contribuir com as extensões ou modificar a configuração padrão.Whenever a ServiceHost is opened and the dispatch runtime is built, all behaviors found either as attributes on contracts, operations, and service implementations or as element in the service configuration are automatically added and subsequently asked to contribute extensions or modify the default configuration.

Para resumir, o trecho de código a seguir mostra apenas a implementação do método ApplyDispatchBehavior , que afeta as alterações de configuração do Dispatcher neste exemplo.For brevity, the following code excerpt only shows the implementation of the method ApplyDispatchBehavior, which effects the configuration changes for the dispatcher in this sample. Os outros métodos não são mostrados porque retornam ao chamador sem fazer nenhum trabalho.The other methods are not shown because they return to the caller without doing any work.

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]  
class DispatchByBodyElementBehaviorAttribute : Attribute, IContractBehavior  
{  
    // public void AddBindingParameters(...)
    // public void ApplyClientBehavior(...)  
    // public void Validate(...)  

Primeiro, a ApplyDispatchBehavior implementação configura o dicionário de pesquisa para o seletor de operação Iterando sobre os OperationDescription elementos nos pontos de extremidade de serviço ContractDescription .First, the ApplyDispatchBehavior implementation sets up the lookup dictionary for the operation selector by iterating over the OperationDescription elements in the service endpoint's ContractDescription. Em seguida, cada descrição da operação é inspecionada para a presença do DispatchBodyElementAttribute comportamento, uma implementação do IOperationBehavior que também é definida neste exemplo.Then, each operation description is inspected for the presence of the DispatchBodyElementAttribute behavior, an implementation of IOperationBehavior that is also defined in this sample. Embora essa classe também seja um comportamento, ela é passiva e não contribui ativamente com nenhuma alteração de configuração no tempo de execução de expedição.While this class is also a behavior, it is passive and does not actively contribute any configuration changes to the dispatch runtime. Todos os seus métodos retornam ao chamador sem realizar nenhuma ação.All of its methods return to the caller without taking any actions. O comportamento da operação existe apenas para que os metadados necessários para o novo mecanismo de expedição, ou seja, o nome qualificado do elemento body em cuja ocorrência de uma operação seja selecionada, possam ser associados às respectivas operações.The operation behavior only exists so that the metadata required for the new dispatch mechanism, namely the qualified name of the body element on whose occurrence an operation is selected, can be associated with the respective operations.

Se esse comportamento for encontrado, um par de valores criado a partir do nome qualificado XML ( QName Propriedade) e o nome da operação ( Name Propriedade) será adicionado ao dicionário.If such a behavior is found, a value pair created from the XML qualified name (QName property) and the operation name (Name property) is added to the dictionary.

Depois que o dicionário é populado, um novo DispatchByBodyElementOperationSelector é construído com essas informações e definido como o seletor de operação do tempo de execução de expedição:Once the dictionary is populated, a new DispatchByBodyElementOperationSelector is constructed with this information and set as the operation selector of the dispatch runtime:

public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)  
{  
    Dictionary<XmlQualifiedName,string> dispatchDictionary =
                     new Dictionary<XmlQualifiedName,string>();  
    foreach( OperationDescription operationDescription in
                              contractDescription.Operations )  
    {  
        DispatchBodyElementAttribute dispatchBodyElement =
   operationDescription.Behaviors.Find<DispatchBodyElementAttribute>();  
        if ( dispatchBodyElement != null )  
        {  
             dispatchDictionary.Add(dispatchBodyElement.QName,
                              operationDescription.Name);  
        }  
    }  
    dispatchRuntime.OperationSelector =
            new DispatchByBodyElementOperationSelector(  
               dispatchDictionary,
               dispatchRuntime.UnhandledDispatchOperation.Name);  
    }  
}  

Implementando o serviçoImplementing the Service

O comportamento implementado neste exemplo afeta diretamente como as mensagens da transmissão são interpretadas e expedidas, que é uma função do contrato de serviço.The behavior implemented in this sample directly affects how messages from the wire are interpreted and dispatched, which is a function of the service contract. Consequentemente, o comportamento deve ser declarado no nível de contrato de serviço em qualquer implementação de serviço que escolha usá-lo.Consequently, the behavior should be declared on the service contract level in any service implementation that chooses to use it.

O serviço de projeto de exemplo aplica o DispatchByBodyElementBehaviorAttribute comportamento do contrato ao IDispatchedByBody contrato de serviço e rotula cada uma das duas operações OperationForBodyA() e OperationForBodyB() com um DispatchBodyElementAttribute comportamento de operação.The sample project service applies the DispatchByBodyElementBehaviorAttribute contract behavior to the IDispatchedByBody service contract and labels each of the two operations OperationForBodyA() and OperationForBodyB() with a DispatchBodyElementAttribute operation behavior. Quando um host de serviço para um serviço que implementa esse contrato é aberto, esses metadados são coletados pelo Dispatcher Builder conforme descrito anteriormente.When a service host for a service that implements this contract is opened, this metadata is picked up by the dispatcher builder as previously described.

Como o seletor de operação é expedido exclusivamente com base no elemento do corpo da mensagem e ignora a "ação", é necessário informar ao tempo de execução para não verificar o cabeçalho "ação" nas respostas retornadas atribuindo o curinga "*" à ReplyAction propriedade de OperationContractAttribute .Because the operation selector dispatches solely based on the message body element and ignores the "Action", it is required to tell the runtime not to check the "Action" header on the returned replies by assigning the wildcard "*" to the ReplyAction property of OperationContractAttribute. Além disso, é necessário ter uma operação padrão que tenha a propriedade "Action" definida como o curinga " * ".Furthermore, it is required to have a default operation that has the "Action" property set to the wildcard "*". A operação padrão recebe todas as mensagens que não podem ser expedidas e não tem um DispatchBodyElementAttribute :The default operation receives all messages which cannot be dispatched and does not have a DispatchBodyElementAttribute:

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples"),  
                            DispatchByBodyElementBehavior]  
public interface IDispatchedByBody  
{  
    [OperationContract(ReplyAction="*"),
     DispatchBodyElement("bodyA","http://tempuri.org")]  
    Message OperationForBodyA(Message msg);  
    [OperationContract(ReplyAction = "*"),
     DispatchBodyElement("bodyB", "http://tempuri.org")]  
    Message OperationForBodyB(Message msg);  
    [OperationContract(Action="*", ReplyAction="*")]  
    Message DefaultOperation(Message msg);  
}  

A implementação do serviço de exemplo é simples.The sample service implementation is straightforward. Cada método encapsula a mensagem recebida em uma mensagem de resposta e a ecoa de volta para o cliente.Every method wraps the received message into a reply message and echoes it back to the client.

Executando e compilando o exemploRunning and Building the Sample

Quando você executa o exemplo, o conteúdo do corpo das respostas da operação é exibido na janela do console do cliente semelhante à saída (formatada) a seguir.When you run the sample, the body content of the operation responses are displayed in the client console window similar to the following (formatted) output.

O cliente envia três mensagens para o serviço cujo elemento de conteúdo do corpo é nomeado bodyA , bodyB e bodyX , respectivamente.The client sends three messages to the service whose body content element is named bodyA, bodyB, and bodyX, respectively. Como pode ser adiado da descrição anterior e do contrato de serviço mostrado, a mensagem de entrada com o bodyA elemento é expedida para o OperationForBodyA() método.As can be deferred from the previous description and the service contract shown, the incoming message with the bodyA element is dispatched to the OperationForBodyA() method. Como não há nenhum destino de expedição explícito para a mensagem com o bodyX elemento body, a mensagem é expedida para o DefaultOperation() .Because there is no explicit dispatch target for the message with the bodyX body element, the message is dispatched to the DefaultOperation(). Cada uma das operações de serviço encapsula o corpo da mensagem recebida em um elemento específico do método e a retorna, o que é feito para correlacionar as mensagens de entrada e saída claramente para este exemplo:Each of the service operations wraps the received message body into an element specific to the method and returns it, which is done to correlate input and output messages clearly for this sample:

<?xml version="1.0" encoding="IBM437"?>  
<replyBodyA xmlns="http://tempuri.org">  
   <q:bodyA xmlns:q="http://tempuri.org">test</q:bodyA>  
</replyBodyA>  
<?xml version="1.0" encoding="IBM437"?>  
<replyBodyB xmlns="http://tempuri.org">  
  <q:bodyB xmlns:q="http://tempuri.org">test</q:bodyB>  
</replyBodyB>  
<?xml version="1.0" encoding="IBM437"?>  
<replyDefault xmlns="http://tempuri.org">  
   <q:bodyX xmlns:q="http://tempuri.org">test</q:bodyX>  
</replyDefault>  

Para configurar, compilar, e executar o exemploTo set up, build, and run the sample

  1. Verifique se você executou o procedimento de configuração única para os exemplos de Windows Communication Foundation.Ensure that you have performed the One-Time Setup Procedure for the Windows Communication Foundation Samples.

  2. Para compilar a solução, siga as instruções em criando os exemplos de Windows Communication Foundation.To build the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  3. Para executar o exemplo em uma configuração de computador único ou cruzado, siga as instruções em executando os exemplos de Windows Communication Foundation.To run the sample in a single- or cross-machine configuration, follow the instructions in Running the Windows Communication Foundation Samples.

Importante

Os exemplos podem já estar instalados no seu computador.The samples may already be installed on your machine. Verifique o seguinte diretório (padrão) antes de continuar.Check for the following (default) directory before continuing.

<InstallDrive>:\WF_WCF_Samples

Se esse diretório não existir, vá para Windows Communication Foundation (WCF) e exemplos de Windows Workflow Foundation (WF) para .NET Framework 4 para baixar todos os Windows Communication Foundation (WCF) e WFWF exemplos.If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF) and WFWF samples. Este exemplo está localizado no seguinte diretório.This sample is located in the following directory.

<InstallDrive>:\WF_WCF_Samples\WCF\Extensibility\Interop\AdvancedDispatchByBody