Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Esempio di transazione Illustra l’uso di una transazione coordinata dal client e le impostazioni di ServiceBehaviorAttribute e OperationBehaviorAttribute per controllare il comportamento delle transazioni di un servizio. Questo esempio si basa sull'esempio introduttivo che implementa un servizio di calcolatrice, ma è esteso per mantenere un log delle operazioni eseguite su un server in una tabella di database e un totale complessivo con stato per le operazioni di calcolatrice. Le scritture rese permanenti nella tabella del registro sul server dipendono dal risultato di una transazione coordinata dal client: se la transazione client non viene completata, la transazione del servizio Web assicura che non venga eseguito il commit degli aggiornamenti al database.
Nota
La procedura di installazione e le istruzioni di compilazione per questo esempio si trovano alla fine di questo articolo.
Nel contratto per il servizio è specificato che tutte le operazioni richiedono che una transazione sia accompagnata dalle richieste.
[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);
}
Per abilitare il flusso della transazione in ingresso, il servizio viene configurato con l'wsHttpBinding fornito dal sistema con l'attributo TransactionFlow impostato su true. Questa associazione utilizza il protocollo WSAtomicTransactionOctober2004 interoperativo:
<bindings>
<wsHttpBinding>
<binding name="transactionalBinding" transactionFlow="true" />
</wsHttpBinding>
</bindings>
Dopo aver iniziato una connessione al servizio e a una transazione, il client accede a numerose operazioni del servizio all'interno dell'ambito di tale transazione e quindi completa la transazione e chiude la connessione:
// 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();
Nel servizio sono presenti tre attribuiti che influiscono sul comportamento delle transazioni del servizio nelle modalità seguenti:
Per
ServiceBehaviorAttribute:La proprietà
TransactionTimeoutspecifica il periodo di tempo entro il quale una transazione deve essere completata. In questo esempio è impostato su 30 secondi.La proprietà
TransactionIsolationLevelspecifica il livello di isolamento supportato dal servizio. Questo valore deve corrispondere al livello di isolamento del client.La proprietà
ReleaseServiceInstanceOnTransactionCompletespecifica se l'istanza del servizio sottostante viene riciclata al completamento di una transazione. Impostando questa proprietà sufalse, il servizio gestisce la stessa istanza del servizio tra varie richieste di operazione. Questo è necessario per mantenere il totale progressivo. Se la proprietà è impostata sutrue, viene generata una nuova istanza dopo ogni azione completata.La proprietà
TransactionAutoCompleteOnSessionClosespecifica se alla chiusura della sessione corrente vengono completate le transazioni in attesa. Impostando questa proprietà sufalse, le singole operazioni devono impostare la proprietà OperationBehaviorAttribute.TransactionAutoComplete sutrueo richiedere in modo esplicito una chiamata al metodo OperationContext.SetTransactionComplete() per completare transazioni. Nell'esempio vengono illustrati entrambi gli approcci.
Su
ServiceContractAttribute:- La proprietà
SessionModespecifica se il servizio correla le richieste appropriate in una sessione logica. Poiché questo servizio include operazioni in cui la proprietà OperationBehaviorAttribute TransactionAutoComplete è impostata sufalse(Multiply e Divide), è necessario specificareSessionMode.Required. Ad esempio, l'operazione Multiply non completa la relativa transazione e si basa su una chiamata successiva a Divide completarla utilizzando il metodoSetTransactionComplete; il servizio deve essere in grado di determinare che queste operazioni sono in esecuzione all'interno della stessa sessione.
- La proprietà
Su
OperationBehaviorAttribute:La proprietà
TransactionScopeRequiredspecifica se le azioni dell'operazione devono essere eseguite all'interno di un ambito della transazione. È impostata sutrueper tutte le operazioni in questo esempio e, poiché il client propaga la relativa transazione a tutte le operazioni, le azioni si verificano all'interno dell'ambito di tale transazione del client.La proprietà
TransactionAutoCompletespecifica la transazione in cui viene eseguito il metodo viene completata automaticamente se non si verificano eccezioni non gestite. Come descritto in precedenza, questa proprietà è impostata sutrueper le operazioni Add e Subtract, e sufalseper le operazioni Multiply e Divide. Le operazioni Add e Subtract completano automaticamente le azioni, mentre Divide completa le azioni tramite una chiamata esplicita al metodoSetTransactionComplete. Multiply non completa le azioni, bensì si basa necessariamente su una chiamata successiva, ad esempio a Divide, per completare le azioni.
L'implementazione del servizio attribuito è come segue:
[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 si esegue l'esempio, le richieste e le risposte dell'operazione vengono visualizzate nella finestra della console client. Premere INVIO nella finestra del client per arrestare il client.
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.
La registrazione delle richieste di operazione del servizio viene visualizzata nella finestra della console del servizio. Premere INVIO nella finestra del client per arrestare il client.
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
Si noti che, oltre a gestire il totale parziale dei calcoli, il servizio segnala la creazione di istanze (un'istanza per questo esempio) e registra le richieste di operazione in un database. Poiché tutte le richieste seguono il flusso della transazione del client, qualsiasi errore nel completamento di tale transazione comporta il rollback di tutte le operazioni del database. Questa situazione può essere dimostrata in vari modi:
Commentare la chiamata del client a
tx.Complete() ed eseguirla di nuovo; questo comporta l'uscita del client dall'ambito della transazione senza completare la transazione.Commentare la chiamata all'operazione del servizio Divide; questo ha come risultato l'impedimento del completamento dell'azione iniziata dall'operazione Multiply e, di conseguenza, la transazione del client alla fine non riesce a completarsi.
Generare un'eccezione non gestita in un punto qualsiasi dell'ambito della transazione del client; anche questa operazione impedisce al client di completare la transazione.
Il risultato di queste procedure è che non viene eseguito il commit di nessuna delle operazioni eseguite all'interno di tale ambito e il conteggio delle righe rese persistenti nel database non viene incrementato.
Nota
Nell'ambito del processo di compilazione, il file di database viene copiato nella cartella bin. È necessario esaminare tale copia del file di database per osservare le righe persistenti nel registro anziché il file incluso nel progetto di Visual Studio.
Per impostare, compilare ed eseguire l'esempio
Verificare di avere installato SQL Server 2005 Express Edition o SQL Server 2005. Nel file App.config del servizio, può essere impostato il database
connectionStringoppure le interazioni del database possono essere disabilitate impostando il valoreusingSqldi appSettings sufalse.Per compilare l'edizione in C# o Visual Basic .NET della soluzione, seguire le istruzioni in Building the Windows Communication Foundation Samples.
Per eseguire l'esempio in un solo computer o tra computer diversi, seguire le istruzioni in Esecuzione degli esempi di Windows Communication Foundation.
Se si esegue l'esempio tra computer, è necessario configurare Microsoft Distributed Transaction Coordinator (MSDTC) per abilitare il flusso delle transazioni di rete e usare lo strumento WsatConfig.exe per abilitare il supporto di rete delle transazioni di Windows Communication Foundation (WCF).
Per configurare Microsoft Distributed Transaction Coordinator (MSDTC) allo scopo di supportare l'esecuzione dell'esempio tra diversi computer
Nel computer del servizio configurare MSDTC per consentire le transazioni di rete in ingresso.
Fare clic sul pulsante Start, scegliere Pannello di controllo, quindi Strumenti di amministrazione e infine Servizi componenti.
Fare clic con il pulsante destro del mouse su Risorse del computer e scegliere Proprietà.
Nella scheda MSDTC fare clic su Configurazione sicurezza.
Selezionare Accesso di rete DTC e Consenti connessioni in ingresso.
Fare clic su Sì per riavviare il servizio MS DTC e quindi fare clic su OK.
Scegliere OK per chiudere la finestra di dialogo.
Sulla macchina di servizio e sulla macchina client, configurare il Windows Firewall per includere il Microsoft Distributed Transaction Coordinator (MSDTC) nell'elenco delle applicazioni permesse:
Eseguire l'applicazione Windows Firewall dal Pannello di controllo.
Nella scheda Eccezioni, scegliere Aggiungi programma.
Passare alla cartella C:\WINDOWS\System32.
Selezionare Msdtc.exe e fare clic su Apri.
Fare clic su OK per chiudere la finestra di dialogo Aggiungi programma e fare di nuovo clic su OK per chiudere l'applet di Windows Firewall.
Nel computer client configurare MSDTC per consentire le transazioni di rete in uscita.
Fare clic sul pulsante Start, scegliere Pannello di controllo, quindi Strumenti di amministrazione e infine Servizi componenti.
Fare clic con il pulsante destro del mouse su Risorse del computer e scegliere Proprietà.
Nella scheda MSDTC fare clic su Configurazione sicurezza.
Selezionare Accesso di rete DTC e Consenti connessioni in uscita.
Fare clic su Sì per riavviare il servizio MS DTC e quindi fare clic su OK.
Scegliere OK per chiudere la finestra di dialogo.