Comportamento de transação de serviço
O exemplo Transações demonstra o uso de uma transação coordenada pelo cliente e as configurações de ServiceBehaviorAttribute e OperationBehaviorAttribute para controlar o comportamento da transação de serviço. Este exemplo é baseado no exemplo de Introdução que implementa um serviço de calculadora, mas é estendido para manter um log do servidor das operações executadas em uma tabela de banco de dados e um total acumulado com estado para as operações da calculadora. As gravações persistentes na tabela de log do servidor dependem do resultado de uma transação coordenada pelo cliente. Se a transação do cliente não for concluída, a transação do serviço Web garantirá que as atualizações do banco de dados não sejam confirmadas.
Observação
O procedimento de instalação e as instruções de compilação dessa amostra estão localizadas no final deste artigo.
O contrato para o serviço define que todas as operações exigem que uma transação tenha um fluxo com as solicitações:
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples",
SessionMode = SessionMode.Required)]
public interface ICalculator
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
double Add(double n);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
double Subtract(double n);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
double Multiply(double n);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
double Divide(double n);
}
Para habilitar o fluxo de transação de entrada, o serviço é configurado com o wsHttpBinding fornecido pelo sistema com o atributo transactionFlow definido como true
. Essa associação usa o protocolo interoperável WSAtomicTransactionOctober2004:
<bindings>
<wsHttpBinding>
<binding name="transactionalBinding" transactionFlow="true" />
</wsHttpBinding>
</bindings>
Depois de iniciar uma conexão com o serviço e uma transação, o cliente acessa várias operações de serviço dentro do escopo dessa transação e, em seguida, conclui a transação e fecha a conexão:
// Create a client
CalculatorClient client = new CalculatorClient();
// Create a transaction scope with the default isolation level of Serializable
using (TransactionScope tx = new TransactionScope())
{
Console.WriteLine("Starting transaction");
// Call the Add service operation.
double value = 100.00D;
Console.WriteLine(" Adding {0}, running total={1}",
value, client.Add(value));
// Call the Subtract service operation.
value = 45.00D;
Console.WriteLine(" Subtracting {0}, running total={1}",
value, client.Subtract(value));
// Call the Multiply service operation.
value = 9.00D;
Console.WriteLine(" Multiplying by {0}, running total={1}",
value, client.Multiply(value));
// Call the Divide service operation.
value = 15.00D;
Console.WriteLine(" Dividing by {0}, running total={1}",
value, client.Divide(value));
Console.WriteLine(" Completing transaction");
tx.Complete();
}
Console.WriteLine("Transaction committed");
// Closing the client gracefully closes the connection and cleans up resources
client.Close();
No serviço, há três atributos que afetam o comportamento da transação de serviço e o fazem das seguintes maneiras:
No
ServiceBehaviorAttribute
:A propriedade
TransactionTimeout
especifica o período dentro do qual uma transação precisa ser concluída. Neste exemplo, ele é definido como 30 segundos.A propriedade
TransactionIsolationLevel
especifica o nível de isolamento compatível com o serviço. Isso é necessário para corresponder ao nível de isolamento do cliente.A propriedade
ReleaseServiceInstanceOnTransactionComplete
especifica se a instância de serviço é reciclada quando a transação é concluída. Ao defini-la comofalse
, o serviço mantém a mesma instância de serviço entre as solicitações de operação. Isso é necessário para manter o total em execução. Se definido comotrue
, uma nova instância é gerada após cada ação concluída.A propriedade
TransactionAutoCompleteOnSessionClose
especifica se as transações pendentes são concluídas quando a sessão é fechada. Ao defini-la comofalse
, as operações individuais são necessárias para definir a propriedadeOperationBehaviorAttribute.TransactionAutoComplete comotrue
ou exigir explicitamente uma chamada ao método OperationContext.SetTransactionComplete() para concluir transações. Este exemplo demonstra ambas as abordagens.
No
ServiceContractAttribute
:- A propriedade
SessionMode
especifica se o serviço correlaciona as solicitações apropriadas em uma sessão lógica. Como esse serviço inclui operações em que a propriedade OperationBehaviorAttribute TransactionAutoComplete é definida comofalse
(Multiplicar e Dividir),SessionMode.Required
precisa ser especificado. Por exemplo, a operação Multiplicar não conclui sua transação e, em vez disso, depende de uma chamada posterior para Dividir para concluir usando o métodoSetTransactionComplete
. O serviço precisa ser capaz de determinar que essas operações estão ocorrendo dentro da mesma sessão.
- A propriedade
No
OperationBehaviorAttribute
:A propriedade
TransactionScopeRequired
especifica se as ações da operação devem ser executadas dentro de um escopo de transação. Isso é definido comotrue
para todas as operações neste exemplo e, como o cliente faz o fluxo da transação para todas as operações, as ações ocorrem dentro do escopo dessa transação de cliente.A propriedade
TransactionAutoComplete
especifica se a transação na qual o método é executado será concluída automaticamente caso nenhuma exceção sem tratamento ocorra. Conforme descrito anteriormente, isso é definido comotrue
para as operações Adicionar e Subtrair, masfalse
para as operações Multiplicar e Dividir. As operações Adicionar e Subtrair completam as ações automaticamente, a Divisão conclui as ações por meio de uma chamada explícita ao métodoSetTransactionComplete
e a Multiplicação não conclui as ações, mas depende e requer uma chamada posterior, como dividir, para concluí-las.
A implementação do serviço atribuído é a seguinte:
[ServiceBehavior(
TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable,
TransactionTimeout = "00:00:30",
ReleaseServiceInstanceOnTransactionComplete = false,
TransactionAutoCompleteOnSessionClose = false)]
public class CalculatorService : ICalculator
{
double runningTotal;
public CalculatorService()
{
Console.WriteLine("Creating new service instance...");
}
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public double Add(double n)
{
RecordToLog(String.Format(CultureInfo.CurrentCulture, "Adding {0} to {1}", n, runningTotal));
runningTotal = runningTotal + n;
return runningTotal;
}
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public double Subtract(double n)
{
RecordToLog(String.Format(CultureInfo.CurrentCulture, "Subtracting {0} from {1}", n, runningTotal));
runningTotal = runningTotal - n;
return runningTotal;
}
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public double Multiply(double n)
{
RecordToLog(String.Format(CultureInfo.CurrentCulture, "Multiplying {0} by {1}", runningTotal, n));
runningTotal = runningTotal * n;
return runningTotal;
}
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
public double Divide(double n)
{
RecordToLog(String.Format(CultureInfo.CurrentCulture, "Dividing {0} by {1}", runningTotal, n));
runningTotal = runningTotal / n;
OperationContext.Current.SetTransactionComplete();
return runningTotal;
}
// Logging method omitted for brevity
}
Quando você executa a amostra, 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
Performing calculations...
Adding 100, running total=100
Subtracting 45, running total=55
Multiplying by 9, running total=495
Dividing by 15, running total=33
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 service.
Creating new service instance...
Writing row 1 to database: Adding 100 to 0
Writing row 2 to database: Subtracting 45 from 100
Writing row 3 to database: Multiplying 55 by 9
Writing row 4 to database: Dividing 495 by 15
Observe que, além de manter o total em execução dos cálculos, o serviço relata a criação de instâncias (uma instância para este exemplo) e registra as solicitações de operação em um banco de dados. Como todas as solicitações fluem a transação do cliente, qualquer falha na conclusão dessa transação resulta na reversão de todas as operações de banco de dados. Isso pode ser demonstrado de várias maneiras:
Comentar a chamada do cliente para
tx.Complete
() e reexecutar - isso resulta na saída do escopo da transação do cliente sem concluir sua transação.Comentar a chamada para a operação de serviço Dividir - isso impede que a ação iniciada pela operação Multiplicação seja concluída e, portanto, a transação do cliente também não é concluída.
Gerear uma exceção sem tratamento em qualquer lugar no escopo de transação do cliente - isso também impede que o cliente complete a transação.
O resultado de qualquer uma delas é que nenhuma das operações executadas dentro desse escopo é confirmada e a contagem de linhas mantidas no banco de dados não aumenta.
Observação
Como parte do processo de build, o arquivo de banco de dados é copiado para a pasta bin. Você precisa examinar essa cópia do arquivo de banco de dados para observar as linhas mantidas no log em vez do arquivo incluído no projeto do Visual Studio.
Para configurar, compilar, e executar o exemplo
Verifique se você instalou o SQL Server 2005 Express Edition ou o SQL Server 2005. No arquivo App.config do serviço, o banco de dados
connectionString
pode ser definido ou as interações de banco de dados podem ser desabilitadas definindo o valorusingSql
de appSettings comofalse
.Para compilar a edição C# ou do Visual Basic .NET da solução, siga as instruções em Como compilar as amostras do Windows Communication Foundation.
Para executar o exemplo em uma configuração de um único computador ou de vários computadores, siga as instruções em Como executar os exemplos do Windows Communication Foundation.
Se você executar o exemplo entre computadores, 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 a compatibilidade com rede de transações do WCF (Windows Communication Foundation).
Para configurar o MSDTC (Coordenador de Transações Distribuídas da Microsoft) para permitir a execução da amostra entre computadores
No computador de serviço, configure o MSDTC para permitir transações de rede de entrada.
No menu Iniciar, navegue até Painel de Controle, Ferramentas Administrativas e, em seguida, Serviços de Componentes.
Clique com o botão direito do mouse em Meu Computador e, em seguida, selecione Propriedades.
Na guia MSDTC, clique em Configuração de Segurança.
Verifique o Acesso do DTC de Rede e Permita a entrada.
Clique em Sim para reiniciar o serviço MS DTC e clique em OK.
Clique em OK para fechar a caixa de diálogo.
No computador de serviço e no computador cliente, configure o Firewall do Windows para incluir o MSDTC (Coordenador de Transações Distribuídas da Microsoft) na lista de aplicativos exceto:
Executar o aplicativo firewall do Windows de Painel de Controle.
Na guia Exceções, clique em Adicionar programa.
Vá para a pasta C:\WINDOWS\System32.
Selecione Msdtc.exe e clique em Abrir.
Clique em OK para fechar a caixa de diálogo Adicionar pograma e clique em OK novamente para fechar o applet do Firewall do Windows.
No computador cliente, configure o MSDTC para permitir transações de rede de saída:
No menu Iniciar, navegue até Painel de Controle, Ferramentas Administrativas e, em seguida, Serviços de Componentes.
Clique com o botão direito do mouse em Meu Computador e, em seguida, selecione Propriedades.
Na guia MSDTC, clique em Configuração de Segurança.
Verifique o Acesso do DTC de Rede e Permita a saída.
Clique em Sim para reiniciar o serviço MS DTC e clique em OK.
Clique em OK para fechar a caixa de diálogo.