Compartilhar via


Sessões e filas

O exemplo de sessão demonstra como enviar e receber um conjunto de mensagens relacionadas na comunicação na fila por meio do transporte MSMQ (Enfileiramento de Mensagens). Este exemplo usa a associação netMsmqBinding. O serviço é um aplicativo de console auto-hospedado para permitir que você observe o serviço que está recebendo mensagens na fila.

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.

Às vezes, um cliente envia um conjunto de mensagens relacionadas entre si em um grupo. Quando as mensagens precisam ser processadas em conjunto ou em uma ordem especificada, uma fila pode ser usada para agrupá-las, para processamento por um só aplicativo de recebimento. Isso é particularmente importante quando há vários aplicativos de recebimento em um grupo de servidores, e é necessário garantir que um grupo de mensagens seja processado pelo mesmo aplicativo de recebimento. As sessões na fila são um mecanismo usado para enviar e receber um conjunto relacionado de mensagens que precisam ser processadas de uma só vez. As sessões na fila exigem uma transação para exibir esse padrão.

No exemplo, o cliente envia várias mensagens para o serviço como parte de uma sessão dentro do escopo de uma só transação.

O contrato de serviço é IOrderTaker, que define um serviço unidirecional adequado para uso com filas. O SessionMode usado no contrato mostrado no código de exemplo a seguir indica que as mensagens fazem parte da sessão.

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)]
public interface IOrderTaker
{
    [OperationContract(IsOneWay = true)]
    void OpenPurchaseOrder(string customerId);

    [OperationContract(IsOneWay = true)]
    void AddProductLineItem(string productId, int quantity);

    [OperationContract(IsOneWay = true)]
    void EndPurchaseOrder();
}

O serviço define as operações de serviço de maneira que a primeira operação seja inscrita em uma transação, mas não conclua automaticamente a transação. As operações seguintes também se inscrevem na mesma transação, mas não são concluídas automaticamente. A última operação na sessão conclui automaticamente a transação. Assim, a mesma transação é usada para várias invocações de operação no contrato de serviço. Se uma das operações gerar uma exceção, a transação será revertida e a sessão será colocada novamente na fila. Após a conclusão bem-sucedida da última operação, a transação é confirmada. O serviço usa PerSession como o InstanceContextMode para receber todas as mensagens em uma sessão na mesma instância do serviço.

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class OrderTakerService : IOrderTaker
{
    PurchaseOrder po;

    [OperationBehavior(TransactionScopeRequired = true,
                                 TransactionAutoComplete = false)]
    public void OpenPurchaseOrder(string customerId)
    {
        Console.WriteLine("Creating purchase order");
        po = new PurchaseOrder(customerId);
    }

    [OperationBehavior(TransactionScopeRequired = true,
                                  TransactionAutoComplete = false)]
    public void AddProductLineItem(string productId, int quantity)
    {
        po.AddProductLineItem(productId, quantity);
        Console.WriteLine("Product " + productId + " quantity " +
                            quantity + " added to purchase order");
    }

    [OperationBehavior(TransactionScopeRequired = true,
                                  TransactionAutoComplete = true)]
    public void EndPurchaseOrder()
    {
       Console.WriteLine("Purchase Order Completed");
       Console.WriteLine();
       Console.WriteLine(po.ToString());
    }
}

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 o código System.Messaging para verificar a existência da fila e a cria, se necessário. O nome da fila é lido do arquivo de configuração por meio da classe AppSettings.

// Host the service within this EXE console application.
public static void Main()
{
    // Get MSMQ queue name from app settings 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 OrderTakerService type.
    using (ServiceHost serviceHost = new ServiceHost(typeof(OrderTakerService)))
    {
        // 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 shutdown the service.
        serviceHost.Close();
    }
}

O nome da fila MSMQ é especificado em uma seção appSettings do arquivo de configuração. O ponto de extremidade do serviço é definido na seção system.serviceModel do arquivo de configuração e especifica a associação netMsmqBinding.

<appSettings>
  <!-- Use appSetting to configure MSMQ queue name. -->
  <add key="queueName" value=".\private$\ServiceModelSamplesSession" />
</appSettings>

<system.serviceModel>
  <services>
    <service name="Microsoft.ServiceModel.Samples.OrderTakerService"
        behaviorConfiguration="CalculatorServiceBehavior">
      ...
      <!-- Define NetMsmqEndpoint -->
      <endpoint address="net.msmq://localhost/private/ServiceModelSamplesSession"
                binding="netMsmqBinding"
                contract="Microsoft.ServiceModel.Samples.IOrderTaker" />
      ...
    </service>
  </services>
  ...
</system.serviceModel>

O cliente cria um escopo de transação. Todas as mensagens da sessão são enviadas para a fila dentro do escopo da transação, fazendo com que elas sejam tratadas como uma unidade atômica em que todas as mensagens são bem-sucedidas ou falham. A transação é confirmada com uma chamada a Complete.

//Create a transaction scope.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
    // Create a client with given client endpoint configuration.
    OrderTakerClient client = new OrderTakerClient("OrderTakerEndpoint");
    // Open a purchase order.
    client.OpenPurchaseOrder("somecustomer.com");
    Console.WriteLine("Purchase Order created");

    // Add product line items.
    Console.WriteLine("Adding 10 quantities of blue widget");
    client.AddProductLineItem("Blue Widget", 10);

    Console.WriteLine("Adding 23 quantities of red widget");
    client.AddProductLineItem("Red Widget", 23);

    // Close the purchase order.
    Console.WriteLine("Closing the purchase order");
    client.EndPurchaseOrder();

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

    // Complete the transaction.
    scope.Complete();
}

Observação

Só é possível usar uma transação individual para todas as mensagens na sessão, e todas as mensagens na sessão precisam ser enviadas antes da confirmação da transação. O fechamento do cliente fecha a sessão. Portanto, o cliente precisa ser fechado antes que a transação seja concluída para enviar todas as mensagens na sessão 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. 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.

No cliente.

Purchase Order created
Adding 10 quantities of blue widget
Adding 23 quantities of red widget
Closing the purchase order

Press <ENTER> to terminate client.

No serviço.

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

Creating purchase order
Product Blue Widget quantity 10 added to purchase order
Product Red Widget quantity 23 added to purchase order
Purchase Order Completed

Purchase Order: 7c86fef0-2306-4c51-80e6-bcabcc1a6e5e
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 10 of Blue Widget @unit price: $2985
                Order LineItem: 23 of Red Widget @unit price: $156
        Total cost of this order: $33438
        Order status: Pending

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. Para compilar a edição C#, C++ ou Visual Basic .NET da solução, siga as instruções em Como compilar os exemplos do Windows Communication Foundation.

  3. Para executar o exemplo em uma configuração de computador único ou cruzado, siga as instruções em Como executar os exemplos 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, ou seja, 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 precisa 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.

Executar o exemplo em um computador ingressado em um grupo de trabalho

  1. Se o computador não fizer parte de um domínio ou não tiver a integração do 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 configuração a seguir.

    <system.serviceModel>
      <services>
        <service name="Microsoft.ServiceModel.Samples.OrderTakerService"
                 behaviorConfiguration="OrderTakerServiceBehavior">
          <host>
            <baseAddresses>
              <add baseAddress=
             "http://localhost:8000/ServiceModelSamples/service"/>
            </baseAddresses>
          </host>
          <!-- Define NetMsmqEndpoint -->
          <endpoint
              address=
            "net.msmq://localhost/private/ServiceModelSamplesSession"
              binding="netMsmqBinding"
              bindingConfiguration="Binding1"
           contract="Microsoft.ServiceModel.Samples.IOrderTaker" />
          <!-- 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="OrderTakerServiceBehavior">
              <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

    A configuração do modo de segurança como None é equivalente à configuração de MsmqAuthenticationMode, de MsmqProtectionLevel e de segurança de Message como None.