Partilhar via


Correlação de mensagens

O exemplo MessageCorrelation demonstra como um aplicativo de enfileiramento de mensagens (MSMQ) pode enviar uma mensagem MSMQ para um serviço WCF (Windows Communication Foundation) e como as mensagens podem ser correlacionadas entre aplicativos remetente e recetor em um cenário de solicitação/resposta. Este exemplo usa a associação msmqIntegrationBinding. O serviço, neste caso, é um aplicativo de console auto-hospedado para permitir que você observe o serviço que recebe mensagens em fila. mil

O serviço processa a mensagem recebida do remetente e envia uma mensagem de resposta de volta ao remetente. O remetente correlaciona a resposta recebida com a solicitação enviada originalmente. As MessageID propriedades e CorrelationID da mensagem são usadas para correlacionar as mensagens de solicitação e resposta.

O IOrderProcessor contrato de serviço define uma operação de serviço unidirecional que é adequada para uso com filas. Uma mensagem MSMQ não tem um cabeçalho Action, portanto, não é possível mapear mensagens MSMQ diferentes para contratos de operação automaticamente. Portanto, só pode haver um contrato de operação neste caso. Se você quiser definir mais contratos de operação no serviço, o aplicativo deve fornecer informações sobre qual cabeçalho na mensagem MSMQ (por exemplo, o rótulo ou correlationID) pode ser usado para decidir qual contrato de operação despachar.

A mensagem MSMQ também não contém informações sobre quais cabeçalhos são mapeados para os diferentes parâmetros do contrato de operação. Portanto, pode haver apenas um parâmetro no contrato de operação. O parâmetro é do tipo MsmqMessage<T>, que contém a mensagem MSMQ subjacente. O tipo "T" na MsmqMessage<T> classe representa os dados que são serializados no corpo da mensagem MSMQ. Neste exemplo, o PurchaseOrder tipo é serializado no corpo da mensagem MSMQ.

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
[ServiceKnownType(typeof(PurchaseOrder))]
public interface IOrderProcessor
{
    [OperationContract(IsOneWay = true, Action = "*")]
    void SubmitPurchaseOrder(MsmqMessage<PurchaseOrder> msg);
}

A operação de serviço processa a ordem de compra e exibe o conteúdo da ordem de compra e seu status na janela do console de serviço. O OperationBehaviorAttribute configura a operação para se alistar em uma transação com a fila e marcar a transação concluída quando a operação retorna. O PurchaseOrder contém os detalhes do pedido que devem ser processados pelo serviço.

// Service class that implements the service contract.
public class OrderProcessorService : IOrderProcessor
{
   [OperationBehavior(TransactionScopeRequired = true,
          TransactionAutoComplete = true)]
   public void SubmitPurchaseOrder(MsmqMessage<PurchaseOrder> ordermsg)
   {
       PurchaseOrder po = (PurchaseOrder)ordermsg.Body;
       Random statusIndexer = new Random();
       po.Status = PurchaseOrder.OrderStates[statusIndexer.Next(3)];
       Console.WriteLine("Processing {0} ", po);
       //Send a response to the client that the order has been received
       // and is pending fulfillment.
       SendResponse(ordermsg);
    }

    private void SendResponse(MsmqMessage<PurchaseOrder> ordermsg)
    {
        OrderResponseClient client = new OrderResponseClient("OrderResponseEndpoint");

        //Set the correlation ID such that the client can correlate the response to the order.
        MsmqMessage<PurchaseOrder> orderResponseMsg = new MsmqMessage<PurchaseOrder>(ordermsg.Body);
        orderResponseMsg.CorrelationId = ordermsg.Id;
        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
        {
            client.SendOrderResponse(orderResponseMsg);
            scope.Complete();
        }

        client.Close();
    }
}

O serviço usa um cliente OrderResponseClient personalizado para enviar a mensagem MSMQ para a fila. Como o aplicativo que recebe e processa a mensagem é um aplicativo MSMQ e não um aplicativo WCF, não há nenhum contrato de serviço implícito entre os dois aplicativos. Portanto, não podemos criar um proxy usando a ferramenta Svcutil.exe neste cenário.

O proxy personalizado é essencialmente o mesmo para todos os aplicativos WCF que usam a msmqIntegrationBinding associação para enviar mensagens. Ao contrário de outros proxies, ele não inclui uma gama de operações de serviço. É apenas uma operação de envio de mensagem.

[System.ServiceModel.ServiceContractAttribute(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IOrderResponse
{

    [System.ServiceModel.OperationContractAttribute(IsOneWay = true, Action = "*")]
    void SendOrderResponse(MsmqMessage<PurchaseOrder> msg);
}

public partial class OrderResponseClient : System.ServiceModel.ClientBase<IOrderResponse>, IOrderResponse
{

    public OrderResponseClient()
    { }

    public OrderResponseClient(string configurationName)
        : base(configurationName)
    { }

    public OrderResponseClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress address)
        : base(binding, address)
    { }

    public void SendOrderResponse(MsmqMessage<PurchaseOrder> msg)
    {
        base.Channel.SendOrderResponse(msg);
    }
}

O serviço é auto-hospedado. Ao usar o transporte de integração MSMQ, a fila usada deve ser criada com antecedência. Isso pode ser feito manualmente ou através de código. Neste exemplo, o serviço contém System.Messaging código para verificar a existência da fila e criá-la, se necessário. O nome da fila é lido a partir do arquivo de configuração.

public static void Main()
{
       // Get the MSMQ queue name from application settings in configuration.
      string queueName =
                ConfigurationManager.AppSettings["orderQueueName"];
      // Create the transacted MSMQ queue if necessary.
      if (!MessageQueue.Exists(queueName))
                MessageQueue.Create(queueName, true);
     // Create a ServiceHost for the OrderProcessorService type.
     using (ServiceHost serviceHost = new
                   ServiceHost(typeof(OrderProcessorService)))
     {
            serviceHost.Open();
            // The service can now be accessed.
            Console.WriteLine("The service is ready.");
            Console.WriteLine("Press <ENTER> to terminate service.");
            Console.ReadLine();
            // Close the ServiceHost to shutdown the service.
            serviceHost.Close();
      }
}

A fila MSMQ para a qual as solicitações de ordem são enviadas é especificada na seção appSettings do arquivo de configuração. Os pontos de extremidade do cliente e do serviço são definidos na seção system.serviceModel do arquivo de configuração. Ambos especificam a msmqIntegrationBinding ligação.

<appSettings>
  <add key="orderQueueName" value=".\private$\Orders" />
</appSettings>

<system.serviceModel>
  <client>
    <endpoint    name="OrderResponseEndpoint"
              address="msmq.formatname:DIRECT=OS:.\private$\OrderResponse"
              binding="msmqIntegrationBinding"
              bindingConfiguration="OrderProcessorBinding"
              contract="Microsoft.ServiceModel.Samples.IOrderResponse">
    </endpoint>
  </client>

  <services>
    <service
      name="Microsoft.ServiceModel.Samples.OrderProcessorService">
      <endpoint address="msmq.formatname:DIRECT=OS:.\private$\Orders"
                            binding="msmqIntegrationBinding"
                bindingConfiguration="OrderProcessorBinding"
                contract="Microsoft.ServiceModel.Samples.IOrderProcessor">
      </endpoint>
    </service>
  </services>

  <bindings>
    <msmqIntegrationBinding>
      <binding name="OrderProcessorBinding" >
        <security mode="None" />
      </binding>
    </msmqIntegrationBinding>
  </bindings>

</system.serviceModel>

O aplicativo cliente usa System.Messaging para enviar uma mensagem durável e transacional para a fila. O corpo da mensagem contém a ordem de compra.

static void PlaceOrder()
{
    //Connect to the queue
    MessageQueue orderQueue =
            new MessageQueue(
                    ConfigurationManager.AppSettings["orderQueueName"])
    // Create the purchase order.
    PurchaseOrder po = new PurchaseOrder();
    po.CustomerId = "somecustomer.com";
    po.PONumber = Guid.NewGuid().ToString();
    PurchaseOrderLineItem lineItem1 = new PurchaseOrderLineItem();
    lineItem1.ProductId = "Blue Widget";
    lineItem1.Quantity = 54;
    lineItem1.UnitCost = 29.99F;

    PurchaseOrderLineItem lineItem2 = new PurchaseOrderLineItem();
    lineItem2.ProductId = "Red Widget";
    lineItem2.Quantity = 890;
    lineItem2.UnitCost = 45.89F;

    po.orderLineItems = new PurchaseOrderLineItem[2];
    po.orderLineItems[0] = lineItem1;
    po.orderLineItems[1] = lineItem2;

    Message msg = new Message();
    msg.UseDeadLetterQueue = true;
    msg.Body = po;

    //Create a transaction scope.
    using (TransactionScope scope = new
     TransactionScope(TransactionScopeOption.Required))
    {
        // Submit the purchase order.
        orderQueue.Send(msg, MessageQueueTransactionType.Automatic);
        // Complete the transaction.
        scope.Complete();
    }
    //Save the messageID for order response correlation.
    orderMessageID = msg.Id;
    Console.WriteLine("Placed the order, waiting for response...");
}

A fila MSMQ da qual as respostas do pedido são recebidas é especificada em uma seção appSettings do arquivo de configuração, conforme mostrado na configuração de exemplo a seguir.

Nota

O nome da fila usa um ponto (.) para o computador local e separadores de barra invertida em seu caminho. O endereço do ponto de extremidade WCF especifica um esquema msmq.formatname e usa "localhost" para o computador local. Um nome de formato formado corretamente segue msmq.formatname no URI de acordo com as diretrizes do MSMQ.

<appSettings>
    <add key=" orderResponseQueueName" value=".\private$\Orders" />
</appSettings>

O aplicativo cliente salva a messageID mensagem de solicitação de pedido que envia ao serviço e aguarda uma resposta do serviço. Quando uma resposta chega na fila, o cliente a correlaciona com a mensagem de pedido enviada usando a correlationID propriedade da mensagem, que contém a messageID mensagem de ordem que o cliente enviou para o serviço originalmente.

static void DisplayOrderStatus()
{
    MessageQueue orderResponseQueue = new
     MessageQueue(ConfigurationManager.AppSettings
                  ["orderResponseQueueName"]);
    //Create a transaction scope.
    bool responseReceived = false;
    orderResponseQueue.MessageReadPropertyFilter.CorrelationId = true;
    while (!responseReceived)
    {
       Message responseMsg;
       using (TransactionScope scope2 = new
         TransactionScope(TransactionScopeOption.Required))
       {
          //Receive the Order Response message.
          responseMsg =
              orderResponseQueue.Receive
                   (MessageQueueTransactionType.Automatic);
          scope2.Complete();
     }
     responseMsg.Formatter = new
     System.Messaging.XmlMessageFormatter(new Type[] {
         typeof(PurchaseOrder) });
     PurchaseOrder responsepo = (PurchaseOrder)responseMsg.Body;
    //Check if the response is for the order placed.
    if (orderMessageID == responseMsg.CorrelationId)
    {
       responseReceived = true;
       Console.WriteLine("Status of current Order: OrderID-{0},Order
            Status-{1}", responsepo.PONumber, responsepo.Status);
    }
    else
    {
       Console.WriteLine("Status of previous Order: OrderID-{0},Order
            Status-{1}", responsepo.PONumber, responsepo.Status);
    }
  }
}

Quando você executa o exemplo, as atividades do cliente e do serviço são exibidas nas janelas do console do serviço e do cliente. Você pode ver o serviço receber mensagens do cliente e enviar uma resposta de volta para o cliente. O cliente exibe a resposta recebida do serviço. Pressione ENTER em cada janela do console para desligar o serviço e o cliente.

Nota

Este exemplo requer a instalação do serviço de enfileiramento de mensagens (MSMQ). Consulte as instruções de instalação do MSMQ na seção Consulte também.

Configurar, compilar e executar o exemplo

  1. Certifique-se de ter executado o procedimento de instalação única para os exemplos do Windows Communication Foundation.

  2. Se o serviço for executado primeiro, ele verificará se a fila está presente. Se a fila não estiver presente, o serviço criará uma. Você pode executar o serviço primeiro para criar a fila ou pode criar uma por meio do Gerenciador de Filas MSMQ. Siga estas etapas para criar uma fila no Windows 2008.

    1. Abra o Gerenciador do Servidor no Visual Studio 2012.

    2. Expanda a guia Recursos .

    3. Clique com o botão direito do mouse em Filas de Mensagens Privadas e selecione Nova, Fila Privada.

    4. Marque a caixa Transacional .

    5. Digite ServiceModelSamplesTransacted como o nome da nova fila.

  3. Para criar a edição C# ou Visual Basic .NET da solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.

  4. Para executar o exemplo em uma configuração de computador único, siga as instruções em Executando os exemplos do Windows Communication Foundation.

Execute o exemplo em todos os computadores

  1. Copie os arquivos do programa de serviço da pasta \service\bin\, na pasta específica do idioma, para o computador de serviço.

  2. Copie os arquivos de programa cliente da pasta \client\bin\, na pasta específica do idioma, para o computador cliente.

  3. No arquivo Client.exe.config, altere orderQueueName para especificar o nome do computador de serviço em vez de ".".

  4. No arquivo Service.exe.config, altere o endereço do ponto de extremidade do cliente para especificar o nome do computador cliente em vez de ".".

  5. No computador de serviço, inicie Service.exe a partir de um prompt de comando.

  6. No computador cliente, inicie Client.exe a partir de um prompt de comando.

Consulte também