Partilhar via


Fluxo de Transações WS

O exemplo TransactionFlow demonstra o uso de uma transação coordenada pelo cliente e as opções do cliente e do servidor para o fluxo de transações usando o protocolo WS-Atomic Transaction ou OleTransactions. Este exemplo é baseado no de Introdução ao que implementa um serviço de calculadora, mas as operações são atribuídas para demonstrar o uso do TransactionFlowAttribute com a enumeração TransactionFlowOption para determinar até que ponto o fluxo de transações está habilitado. Dentro do escopo da transação fluída, 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 garante que as atualizações apropriadas para o banco de dados não sejam confirmadas.

Observação

O procedimento de configuração e as instruções de compilação 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 forma, 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);
}

Isto define as operações pela 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 em progresso por meio da configuração explícita de "NotAllowed".

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

Para habilitar o fluxo de transações, 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 Metadata Exchange. O ponto de extremidade TCP e o ponto de extremidade HTTP usam as seguintes associações, ambas com a propriedade transactionFlow <> habilitada.

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

Observação

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 Windows Communication Foundation (WCF).

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 tomadas dentro do método ocorrem dentro do escopo de uma transação. Nesse caso, as ações tomadas incluem a gravação no banco de dados de log. Se a solicitação de operação incluir uma transação fluída, as ações ocorrerão dentro do escopo da transação de entrada ou um novo escopo de transação será gerado automaticamente.

Observação

A propriedade TransactionScopeRequired define o comportamento local para as implementações do método de serviço e não define a capacidade ou o requisito do cliente para fluir 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 de 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 de propriedade transactionFlow do serviço são refletidas na configuração da aplicação 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");

Observação

O comportamento observado desta amostra é o mesmo, independentemente do protocolo ou transporte escolhido.

Tendo iniciado a conexão com o serviço, o cliente cria uma nova TransactionScope nas chamadas às operações do 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 para o serviço, e as ações do serviço ocorrem no âmbito da transação do cliente.

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

  • A segunda solicitação Subtract é executada dentro de 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 propaga uma transação para o serviço. Essa abordagem permite que um cliente opte explicitamente por não participar e se proteja 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 âmbito de uma transação nova e desconexa.

  • A solicitação de 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 como TransactionFlowOptionNotAllowed.

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

Quando você executa o exemplo, as solicitações de operação e as respostas 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 registo dos pedidos de operação do serviço é apresentado 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 tomadas dentro desse escopo são confirmadas. Especificamente, os 5 registros anotados são mantidos no banco de dados do serviço. Os 2 primeiros ocorreram no âmbito da transação do cliente.

Se uma exceção ocorreu 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. Este efeito pode ser observado repetindo a execução do exemplo depois de comentar a chamada para completar o componente externo TransactionScope. Em tal execução, apenas as últimas 3 ações (do segundo Subtract, as solicitações Multiply e Divide) são registradas porque a transação do cliente não fluiu para elas.

Para configurar, compilar e executar o exemplo

  1. Para criar a versão C# ou Visual Basic .NET da solução, siga as instruções em Criando 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 de máquina única ou cruzada, siga as instruções em Executando os exemplos do Windows Communication Foundation.

    Observação

    Para 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 de Transações WCF. 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, você deve configurar o Microsoft Distributed Transaction Coordinator (MSDTC) para habilitar o fluxo de transações de rede e usar a ferramenta WsatConfig.exe para habilitar o suporte de rede de transações WCF.

Para configurar o Microsoft Distributed Transaction Coordinator (MSDTC) para suportar a execução do exemplo

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

    1. No menu Iniciar, navegue até Painel de Controlo, depois Ferramentas Administrativas, e depois Serviços de Componentes.

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

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

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

    5. Verifique de Acesso DTC à Rede e Permitirde entrada .

    6. Clique em OKe, em seguida, clique em Sim para reiniciar o serviço MSDTC.

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

  2. Em uma máquina 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é Painel de Controle, Ferramentas Administrativase 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 em Coordenador DTC e selecione Propriedades.

    4. Na guia Segurança, marque Acesso DTC à Rede e Permitir entrada.

    5. Clique em OKe, em seguida, clique em Sim para reiniciar o serviço MSDTC.

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

  3. Na máquina cliente, configure o MSDTC para permitir transações de rede de saída:

    1. No menu Iniciar, navegue até Control Panel, Ferramentas Administrativase Serviços de Componentes.

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

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

    4. Verifique o Acesso DTC à Rede e Permitir Saída .

    5. Clique em OKe, em seguida, clique em Sim para reiniciar o serviço MSDTC.

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