Associação transacionada do MSMQ

O exemplo de Transacionada demonstra como executar a comunicação na fila transacionada usando o MSMQ (Enfileiramento de Mensagens).

Observação

Os procedimentos de instalação e as instruções de build desse exemplo estão localizadas no final deste tópico.

Na comunicação na fila, o cliente se comunica com o serviço usando uma fila. Mais precisamente, o cliente envia mensagens para uma fila. O serviço recebe mensagens da fila. Portanto, o serviço e o cliente não precisam estar em execução ao mesmo tempo para se comunicar usando uma fila.

Quando as transações são usadas para enviar e receber mensagens, na verdade, há duas transações separadas. Quando o cliente envia mensagens dentro do escopo de uma transação, a transação é local para o cliente e o gerenciador de filas do cliente. Quando o serviço recebe mensagens no escopo da transação, a transação é local para o serviço e o gerenciador de filas de recebimento. É muito importante lembrar que o cliente e o serviço não estão participando da mesma transação; em vez disso, eles estão usando transações diferentes ao executar suas operações (como enviar e receber) com a fila.

Neste exemplo, o cliente envia um lote de mensagens para o serviço de dentro do escopo de uma transação. As mensagens enviadas para a fila são recebidas pelo serviço dentro do escopo de transação definido pelo serviço.

O contrato de serviço IOrderProcessor, como mostrado no código de amostra a seguir. A interface define um serviço unidirecional adequado para uso com filas.

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public interface IOrderProcessor
{
    [OperationContract(IsOneWay = true)]
    void SubmitPurchaseOrder(PurchaseOrder po);
}

O comportamento do serviço define um comportamento de operação com TransactionScopeRequired definido como true. Isso garante que o mesmo escopo de transação usado para recuperar a mensagem da fila seja usado por todos os gerenciador de recursos acessados pelo método. Também garante que, se o método gerar uma exceção, a mensagem será retornada para a fila. Sem definir esse comportamento de operação, um canal enfileirado cria uma transação para ler a mensagem da fila e a confirma automaticamente antes de expedir de modo que, se a operação falhar, a mensagem será perdida. O cenário mais comum é que as operações de serviço se alistem na transação usada para ler a mensagem da fila, conforme demonstrado no código a seguir.

 // This service class that implements the service contract.
 // This added code writes output to the console window.
public class OrderProcessorService : IOrderProcessor
 {
     [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
     public void SubmitPurchaseOrder(PurchaseOrder po)
     {
         Orders.Add(po);
         Console.WriteLine("Processing {0} ", po);
     }
  …
}

O serviço é auto-hospedado. Quando o transporte MSMQ é usado, a fila usada precisa ser criada com antecedência. Isso pode ser feito manualmente ou por meio do código. Neste exemplo, o serviço contém código para verificar a existência da fila e criar a fila se ela não existir. O nome da fila é lido no arquivo de configuração. O endereço básico é usado pela Ferramenta Utilitário de Metadados ServiceModel (Svcutil.exe) para gerar o proxy para o serviço.

// Host the service within this EXE console application.
public static void Main()
{
    // Get the MSMQ queue name from appSettings in configuration.
    string queueName = ConfigurationManager.AppSettings["queueName"];

    // 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)))
    {
        // Open the ServiceHost to create listeners and start listening for messages.
        serviceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.WriteLine();
        Console.ReadLine();

        // Close the ServiceHost to shut down the service.
        serviceHost.Close();
    }
}

O nome da fila MSMQ é especificado em uma seção appSettings do arquivo de configuração, conforme mostrado na configuração da amostra.

<appSettings>
    <add key="queueName" value=".\private$\ServiceModelSamplesTransacted" />
</appSettings>

Observação

O nome da fila usa um ponto (.) para o computador local e separadores de barra invertida em seu caminho ao criar a fila usando System.Messaging. O ponto de extremidade do WCF (Windows Communication Foundation) usa o endereço da fila com o esquema net.msmq, usa "localhost" para indicar o computador local e usa barras de encaminhamento em seu caminho.

O cliente cria um escopo de transação. A comunicação com a fila ocorre no escopo da transação, fazendo com que ela seja tratada como uma unidade atômica em que todas as mensagens são enviadas para a fila ou nenhuma das mensagens são enviadas para a fila. A transação é confirmada chamando Complete no escopo da transação.

// Create a client.
OrderProcessorClient client = new OrderProcessorClient();

// 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;

// Create a transaction scope.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
    // Make a queued call to submit the purchase order.
    client.SubmitPurchaseOrder(po);
    // Complete the transaction.
    scope.Complete();
}

// Closing the client gracefully closes the connection and cleans up resources.
client.Close();

Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();

Para verificar se as transações estão funcionando, modifique o cliente comentando o escopo da transação, conforme mostrado no código de exemplo a seguir, recompile a solução e execute o cliente.

//scope.Complete();

Como a transação não foi concluída, as mensagens não são enviadas para a fila.

Quando você executa o exemplo, as atividades de cliente e de serviço são exibidas nas janelas do serviço e do console do cliente. Você pode ver o serviço receber mensagens do cliente. Pressione ENTER em cada janela do console para desligar o serviço e o cliente. Observe que, como a fila está em uso, o cliente e o serviço não precisam estar em funcionamento ao mesmo tempo. Você pode executar o cliente, desligá-lo e iniciar o serviço e ele ainda receberá as mensagens.

The service is ready.
Press <ENTER> to terminate service.

Processing Purchase Order: 7b31ce51-ae7c-4def-9b8b-617e4288eafd
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 54 of Blue Widget @unit price: $29.99
                Order LineItem: 890 of Red Widget @unit price: $45.89
        Total cost of this order: $42461.56
        Order status: Pending

Para configurar, compilar, e executar o exemplo

  1. Verifique se você executou 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 do 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 NovaFila Privada.

    4. Marque a caixa Transacional.

    5. Insira ServiceModelSamplesTransacted como o nome da nova fila.

  3. Para compilar a edição .NET do C# ou do Visual Basic da solução, siga as instruções contidas em Como Compilar as Amostras do Windows Communication Foundation.

  4. Para executar o exemplo em uma configuração de computador único ou entre computadores, siga as instruções em Como executar as amostras do Windows Communication Foundation.

Por padrão com a NetMsmqBinding, a segurança do transporte está habilitada. Há duas propriedades relevantes para a segurança do transporte MSMQ, MsmqAuthenticationMode e MsmqProtectionLevel. Por padrão, o modo de autenticação é definido como Windows e o nível de proteção é definido como Sign. Para que o MSMQ forneça o recurso de autenticação e assinatura, ele deve fazer parte de um domínio e a opção de integração do Active Directory para o MSMQ precisa ser instalada. Se você executar este exemplo em um computador que não atenda a esses critérios, receberá um erro.

Para executar o exemplo em um computador que ingressou em um grupo de trabalho ou sem integração com o Active Directory

  1. Se o computador não fizer parte de um domínio ou não tiver a integração com o Active Directory instalada, desative a segurança do transporte definindo o modo de autenticação e o nível de proteção como None, conforme mostrado no exemplo de código de configuração a seguir.

    <system.serviceModel>
      <services>
        <service name="Microsoft.ServiceModel.Samples.OrderProcessorService"
                 behaviorConfiguration="OrderProcessorServiceBehavior">
          <host>
            <baseAddresses>
              <add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
            </baseAddresses>
          </host>
          <!-- Define NetMsmqEndpoint. -->
          <endpoint
              address="net.msmq://localhost/private/ServiceModelSamplesTransacted"
              binding="netMsmqBinding"
              bindingConfiguration="Binding1"
           contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
          <!-- The mex endpoint is exposed at http://localhost:8000/ServiceModelSamples/service/mex. -->
          <endpoint address="mex"
                    binding="mexHttpBinding"
                    contract="IMetadataExchange" />
        </service>
      </services>
    
      <bindings>
        <netMsmqBinding>
          <binding name="Binding1">
            <security mode="None" />
          </binding>
        </netMsmqBinding>
      </bindings>
    
        <behaviors>
          <serviceBehaviors>
            <behavior name="OrderProcessorServiceBehavior">
              <serviceMetadata httpGetEnabled="True"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
    
      </system.serviceModel>
    
  2. Verifique se você alterou a configuração no servidor e no cliente antes de executar o exemplo.

    Observação

    Definir security mode como None é equivalente a configurar a segurança MsmqAuthenticationMode, MsmqProtectionLevel e Message como None.