Compartilhar via


Fluxo de transação do WS

O exemplo TransactionFlow demonstra o uso de uma transação coordenada pelo cliente e as opções de cliente e servidor para o fluxo de transação usando o protocolo WS-Atomic Transaction ou OleTransactions. Esta amostra baseia-se na Introdução que implementa um serviço de calculadora, mas as operações são atribuídas para demonstrar o uso de TransactionFlowAttribute com a enumeração TransactionFlowOption para determinar em que grau o fluxo de transação está habilitado. Dentro do escopo da transação fluida, um log das operações solicitadas é gravado em um banco de dados e persiste até que a transação coordenada pelo cliente seja concluída – se a transação do cliente não for concluída, a transação do serviço Web garantirá que as atualizações apropriadas para o banco de dados não sejam confirmadas.

Nota

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

Depois de iniciar uma conexão com o serviço e uma transação, o cliente acessa várias operações de serviço. O contrato para o serviço é definido da seguinte maneira com cada uma das operações demonstrando uma configuração diferente para o TransactionFlowOption.

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Mandatory)]
    double Add(double n1, double n2);
    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    double Subtract(double n1, double n2);
    [OperationContract]
    [TransactionFlow(TransactionFlowOption.NotAllowed)]
    double Multiply(double n1, double n2);
    [OperationContract]
    double Divide(double n1, double n2);
}

Isso define as operações na ordem em que devem ser processadas:

  • Uma solicitação de operação Add deve incluir uma transação fluída.

  • Uma solicitação de operação Subtract pode incluir uma transação fluída.

  • Uma solicitação de operação Multiply não deve incluir uma transação fluida pela configuração explícita NotAllowed.

  • Uma solicitação de operação Divide não deve incluir uma transação fluída por meio da omissão de um atributo TransactionFlow.

Para habilitar o fluxo de transação, as associações com a propriedade <transactionFlow> habilitada devem ser usadas além dos atributos de operação apropriados. Neste exemplo, a configuração do serviço expõe um ponto de extremidade TCP e um ponto de extremidade HTTP, além de um ponto de extremidade do Exchange de Metadados. O ponto de extremidade TCP e o ponto de extremidade HTTP usam as associações a seguir, ambas com a propriedade <transactionFlow> habilitada.

<bindings>
  <netTcpBinding>
    <binding name="transactionalOleTransactionsTcpBinding"
             transactionFlow="true"
             transactionProtocol="OleTransactions"/>
  </netTcpBinding>
  <wsHttpBinding>
    <binding name="transactionalWsatHttpBinding"
             transactionFlow="true" />
  </wsHttpBinding>
</bindings>

Nota

O netTcpBinding fornecido pelo sistema permite a especificação do transactionProtocol, enquanto o wsHttpBinding fornecido pelo sistema usa apenas o protocolo WSAtomicTransactionOctober2004 mais interoperável. O protocolo OleTransactions só está disponível para uso por clientes do WCF (Windows Communication Foundation).

Para a classe que implementa a interface ICalculator, todos os métodos são atribuídos com TransactionScopeRequired propriedade definida como true. Essa configuração declara que todas as ações executadas dentro do método ocorrem dentro do escopo de uma transação. Nesse caso, as ações executadas incluem a gravação no banco de dados de log. Se a solicitação de operação incluir uma transação fluida, as ações ocorrerão no escopo da transação de entrada ou um novo escopo de transação será gerado automaticamente.

Nota

A propriedade TransactionScopeRequired define o comportamento local nas implementações do método de serviço e não determina a capacidade ou necessidade do cliente de gerir uma transação.

// Service class that implements the service contract.
[ServiceBehavior(TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable)]
public class CalculatorService : ICalculator
{
    [OperationBehavior(TransactionScopeRequired = true)]
    public double Add(double n1, double n2)
    {
        RecordToLog(String.Format(CultureInfo.CurrentCulture, "Adding {0} to {1}", n1, n2));
        return n1 + n2;
    }

    [OperationBehavior(TransactionScopeRequired = true)]
    public double Subtract(double n1, double n2)
    {
        RecordToLog(String.Format(CultureInfo.CurrentCulture, "Subtracting {0} from {1}", n2, n1));
        return n1 - n2;
    }

    [OperationBehavior(TransactionScopeRequired = true)]
    public double Multiply(double n1, double n2)
    {
        RecordToLog(String.Format(CultureInfo.CurrentCulture, "Multiplying {0} by {1}", n1, n2));
        return n1 * n2;
    }

    [OperationBehavior(TransactionScopeRequired = true)]
    public double Divide(double n1, double n2)
    {
        RecordToLog(String.Format(CultureInfo.CurrentCulture, "Dividing {0} by {1}", n1, n2));
        return n1 / n2;
    }

    // Logging method omitted for brevity
}

No cliente, as configurações TransactionFlowOption do serviço nas operações são refletidas na definição gerada pelo cliente da interface ICalculator. Além disso, as configurações da propriedade transactionFlow do serviço são refletidas na configuração do aplicativo do cliente. O cliente pode selecionar o transporte e o protocolo selecionando o endpointConfigurationNameapropriado.

// Create a client using either wsat or oletx endpoint configurations
CalculatorClient client = new CalculatorClient("WSAtomicTransaction_endpoint");
// CalculatorClient client = new CalculatorClient("OleTransactions_endpoint");

Nota

O comportamento observado deste exemplo é o mesmo, independentemente de qual protocolo ou transporte seja escolhido.

Depois de iniciar a conexão com o serviço, o cliente cria um novo TransactionScope em torno das chamadas para as operações de serviço.

// Start a transaction scope
using (TransactionScope tx =
            new TransactionScope(TransactionScopeOption.RequiresNew))
{
    Console.WriteLine("Starting transaction");

    // Call the Add service operation
    //  - generatedClient will flow the required active transaction
    double value1 = 100.00D;
    double value2 = 15.99D;
    double result = client.Add(value1, value2);
    Console.WriteLine("  Add({0},{1}) = {2}", value1, value2, result);

    // Call the Subtract service operation
    //  - generatedClient will flow the allowed active transaction
    value1 = 145.00D;
    value2 = 76.54D;
    result = client.Subtract(value1, value2);
    Console.WriteLine("  Subtract({0},{1}) = {2}", value1, value2, result);

    // Start a transaction scope that suppresses the current transaction
    using (TransactionScope txSuppress =
                new TransactionScope(TransactionScopeOption.Suppress))
    {
        // Call the Subtract service operation
        //  - the active transaction is suppressed from the generatedClient
        //    and no transaction will flow
        value1 = 21.05D;
        value2 = 42.16D;
        result = client.Subtract(value1, value2);
        Console.WriteLine("  Subtract({0},{1}) = {2}", value1, value2, result);

        // Complete the suppressed scope
        txSuppress.Complete();
    }

    // Call the Multiply service operation
    // - generatedClient will not flow the active transaction
    value1 = 9.00D;
    value2 = 81.25D;
    result = client.Multiply(value1, value2);
    Console.WriteLine("  Multiply({0},{1}) = {2}", value1, value2, result);

    // Call the Divide service operation.
    // - generatedClient will not flow the active transaction
    value1 = 22.00D;
    value2 = 7.00D;
    result = client.Divide(value1, value2);
    Console.WriteLine("  Divide({0},{1}) = {2}", value1, value2, result);

    // Complete the transaction scope
    Console.WriteLine("  Completing transaction");
    tx.Complete();
}

Console.WriteLine("Transaction committed");

As chamadas para as operações são as seguintes:

  • A solicitação Add encaminha a transação necessária ao serviço e as ações do serviço acontecem dentro do escopo da transação do cliente.

  • A primeira solicitação Subtract também flui a transação permitida para o serviço e as ações do serviço ocorrem novamente no escopo da transação do cliente.

  • A segunda solicitação Subtract é executada em um novo escopo de transação declarado com a opção TransactionScopeOption.Suppress. Isso suprime a transação externa inicial do cliente e a solicitação não flui uma transação para o serviço. Essa abordagem permite que um cliente opte explicitamente por recusar e proteger contra o fluxo de uma transação para um serviço quando isso não for necessário. As ações do serviço ocorrem no escopo de uma transação nova e não conectada.

  • A solicitação Multiply não encaminha uma transação para o serviço porque a definição gerada pelo cliente da interface ICalculator inclui um TransactionFlowAttribute configurado para TransactionFlowOptionNotAllowed.

  • A solicitação Divide não encaminha uma transação para o serviço porque, mais uma vez, a definição da interface ICalculator gerada pelo cliente não inclui um TransactionFlowAttribute. As ações do serviço ocorrem novamente no escopo de outra transação nova e não conectada.

Quando você executa o exemplo, as solicitações e respostas da operação são exibidas na janela do console do cliente. Pressione ENTER na janela do cliente para desligar o cliente.

Starting transaction
  Add(100,15.99) = 115.99
  Subtract(145,76.54) = 68.46
  Subtract(21.05,42.16) = -21.11
  Multiply(9,81.25) = 731.25
  Divide(22,7) = 3.14285714285714
  Completing transaction
Transaction committed
Press <ENTER> to terminate client.

O log das solicitações de operação de serviço é exibido na janela do console do serviço. Pressione ENTER na janela do cliente para desligar o cliente.

Press <ENTER> to terminate the service.
  Writing row to database: Adding 100 to 15.99
  Writing row to database: Subtracting 76.54 from 145
  Writing row to database: Subtracting 42.16 from 21.05
  Writing row to database: Multiplying 9 by 81.25
  Writing row to database: Dividing 22 by 7

Após uma execução bem-sucedida, o escopo da transação do cliente é concluído e todas as ações executadas dentro desse escopo são confirmadas. Especificamente, os cinco registros observados são mantidos no banco de dados do serviço. Os dois primeiros ocorreram dentro do escopo da transação do cliente.

Se ocorreu uma exceção em qualquer lugar dentro do TransactionScope do cliente, a transação não poderá ser concluída. Isso faz com que os registros registrados nesse escopo não sejam confirmados no banco de dados. Esse efeito pode ser observado repetindo a execução de exemplo depois de comentar a chamada para concluir o TransactionScope exterior. Nessa execução, somente as três últimas ações (do segundo Subtract, do Multiply e das solicitações Divide) são registradas porque a transação do cliente não fluiu para elas.

Para configurar, compilar e executar o exemplo

  1. Para construir a versão C# ou Visual Basic .NET da solução, siga as instruções em Construindo os Exemplos do Windows Communication Foundation.

  2. Verifique se você instalou o SQL Server Express Edition ou o SQL Server e se a cadeia de conexão foi definida corretamente no arquivo de configuração do aplicativo do serviço. Para executar o exemplo sem usar um banco de dados, defina o valor usingSql no arquivo de configuração do aplicativo do serviço como false.

  3. Para executar o exemplo em uma configuração única ou entre máquinas, siga as instruções em Executando os exemplos do Windows Communication Foundation.

    Nota

    Para a configuração entre máquinas, habilite o Coordenador de Transações Distribuídas usando as instruções abaixo e use a ferramenta WsatConfig.exe do SDK do Windows para habilitar o suporte à rede WCF Transactions. Para obter informações sobre como configurar WsatConfig.exe, consulte Configuração do Suporte a Transações WS-Atomic.

Se você executar o exemplo no mesmo computador ou em computadores diferentes, deverá configurar o MSDTC (Coordenador de Transações Distribuídas da Microsoft) para habilitar o fluxo de transações de rede e usar a ferramenta WsatConfig.exe para habilitar o suporte à rede de transações do WCF.

Para configurar o MSDTC (Coordenador de Transações Distribuídas da Microsoft) para dar suporte à execução do exemplo

  1. Em um computador de serviço que executa o Windows Server 2003 ou Windows XP, configure o MSDTC para permitir transações de rede de entrada seguindo estas instruções.

    1. No menu Iniciar , navegue até o Painel de Controle , depois Ferramentas Administrativas , e em seguida, Serviços de Componentes .

    2. Expanda Serviços de Componentes. Abra a pasta Computadores.

    3. Clique com o botão direito do mouse em Meu Computador e, em seguida, selecione Propriedades.

    4. Na guia MSDTC, clique em Configuração de Segurança.

    5. Verifique o Acesso do DTC de Rede e Permita a entrada.

    6. Clique em OK e, em seguida, Sim para reiniciar o serviço MSDTC.

    7. Clique em OK para fechar a caixa de diálogo.

  2. Em um computador de serviço que executa o Windows Server 2008 ou o Windows Vista, configure o MSDTC para permitir transações de rede de entrada seguindo estas instruções.

    1. No menu Iniciar , navegue até o Painel de Controle , depois Ferramentas Administrativas , e em seguida, Serviços de Componentes .

    2. Expanda Serviços de Componentes. Abra a pasta Computadores. Selecione Coordenador de Transações Distribuídas.

    3. Clique com o botão direito do mouse e, Coordenador do DTC e selecione Propriedades.

    4. Na guia Segurança, verifique o Acesso do DTC de Rede e Permita a entrada.

    5. Clique em OK e, em seguida, Sim para reiniciar o serviço MSDTC.

    6. Clique em OK para fechar a caixa de diálogo.

  3. No computador cliente, configure o MSDTC para permitir transações de rede de saída:

    1. No menu Iniciar, navegue até Control Panel, Ferramentas Administrativas e, em seguida, Serviços de Componentes.

    2. Clique com o botão direito do mouse em Meu Computador e, em seguida, selecione Propriedades.

    3. Na guia MSDTC, clique em Configuração de Segurança.

    4. Verifique o Acesso do DTC de Rede e Permita a saída.

    5. Clique em OK e, em seguida, Sim para reiniciar o serviço MSDTC.

    6. Clique em OK para fechar a caixa de diálogo.