Partilhar via


Vinculação MSMQ transacionada

O exemplo Transacted demonstra como executar a comunicação em fila transacionada usando o serviço de enfileiramento de mensagens (MSMQ).

Nota

O procedimento de configuração e as instruções de compilação para este exemplo estão localizados no final deste tópico.

Na comunicação em 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. O serviço e o cliente, portanto, 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 existem 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 dentro do 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 então recebidas pelo serviço dentro do escopo de transação definido pelo serviço.

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

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

O comportamento de 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 gerentes de recursos acessados pelo método. Ele também garante que, se o método lançar 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 do envio, 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. Ao usar o transporte 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 código para verificar a existência da fila e criar a fila se ela não existir. O nome da fila é lido a partir do arquivo de configuração. O endereço base é usado pela ServiceModel Metadata Utility Tool (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 de exemplo a seguir.

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

Nota

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 Windows Communication Foundation (WCF) usa o endereço da fila com esquema net.msmq, usa "localhost" para indicar o computador local e usa barras em seu caminho.

O cliente cria um escopo de transação. A comunicação com a fila ocorre dentro do escopo da transação, fazendo com que ela seja tratada como uma unidade atômica onde todas as mensagens são enviadas para a fila ou nenhuma das mensagens é enviada para a fila. A transação é confirmada chamando Complete o 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, reconstrua a solução e execute o cliente.

//scope.Complete();

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

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. Pressione ENTER em cada janela do console para desligar o serviço e o cliente. Observe que, como o enfileiramento está em uso, o cliente e o serviço não precisam estar funcionando ao mesmo tempo. Você pode executar o cliente, desligá-lo e, em seguida, iniciar o serviço e ele ainda recebe 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. 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 ou entre computadores, siga as instruções em Executando os exemplos do Windows Communication Foundation.

Por padrão, a segurança de transporte está habilitada NetMsmqBinding. Há duas propriedades relevantes para a segurança MsmqAuthenticationMode de transporte MSMQ 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 Ative Directory para MSMQ deve estar instalada. Se você executar este exemplo em um computador que não satisfaz esses critérios, você receberá um erro.

Para executar o exemplo em um computador associado a um grupo de trabalho ou sem integração com o Ative Directory

  1. Se o computador não fizer parte de um domínio ou não tiver a integração com o Ative Directory instalada, desative a segurança de transporte definindo o modo de autenticação e o nível de proteção como None mostrado no código de configuração de exemplo 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. Certifique-se de alterar a configuração no servidor e no cliente antes de executar o exemplo.

    Nota

    Definir security mode como None é equivalente a definir MsmqAuthenticationMode, MsmqProtectionLevele Message segurança para None.