Condividi tramite


Flusso delle transazioni WS

L'esempio TransactionFlow illustra l'uso di una transazione coordinata dal client e delle opzioni client e server per il flusso delle transazioni usando il protocollo WS-Atomic Transaction o OleTransactions. Questo esempio si basa sul Getting Started, che implementa un servizio calcolatrice, ma le operazioni sono assegnate per illustrare l'uso del TransactionFlowAttribute con l'enumerazione TransactionFlowOption per determinare il livello di abilitazione del flusso delle transazioni. Nell'ambito della transazione fluita, un log delle operazioni richieste viene scritto e mantenuto in un database fino al completamento della transazione coordinata dal client. Se la transazione del client non viene completata, la transazione del servizio Web garantisce che gli aggiornamenti appropriati al database non siano convalidati.

Nota

La procedura di installazione e le istruzioni di compilazione per questo esempio si trovano alla fine di questo argomento.

Dopo aver avviato una connessione al servizio e una transazione, il client accede a diverse operazioni del servizio. Il contratto per il servizio è definito come segue, con ciascuna operazione che illustra un'impostazione diversa per il 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);
}

In questo modo vengono definite le operazioni nell'ordine in cui devono essere elaborate:

  • Una richiesta di operazione Add deve includere una transazione elaborata.

  • Una richiesta di operazione Subtract può includere una transazione elaborata.

  • Una richiesta di operazione Multiply non deve includere una transazione propagata tramite l'esplicita impostazione NotAllowed.

  • Una richiesta di operazione Divide non deve includere una transazione propagata tramite l'omissione di un attributo TransactionFlow.

Per abilitare il flusso delle transazioni, è necessario usare le associazioni con la proprietà <transactionFlow> abilitata oltre agli attributi dell'operazione appropriati. In questo esempio, la configurazione del servizio espone un endpoint TCP e un endpoint HTTP oltre a un endpoint di Scambio metadati. L'endpoint TCP e l'endpoint HTTP usano le associazioni seguenti, entrambe con la proprietà <transactionFlow> abilitata.

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

Nota

Il netTcpBinding fornito dal sistema consente di specificare transactionProtocol, mentre il protocollo wsHttpBinding fornito dal sistema usa solo il protocollo WSAtomicTransactionOctober2004 più interoperabile. Il protocollo OleTransactions è disponibile solo per l'uso da parte dei client Windows Communication Foundation (WCF).

Per la classe che implementa l'interfaccia ICalculator, tutti i metodi vengono attribuiti con TransactionScopeRequired proprietà impostata su true. Questa impostazione dichiara che tutte le azioni eseguite all'interno del metodo si verificano nell'ambito di una transazione. In questo caso, le azioni eseguite includono la registrazione nel database di log. Se la richiesta di operazione include una transazione propagata, le azioni vengono eseguite all'interno dell'ambito della transazione in ingresso o viene generato automaticamente un nuovo ambito di transazione.

Nota

La proprietà TransactionScopeRequired definisce il comportamento locale per le implementazioni del metodo del servizio e non definisce la capacità o il requisito del client di gestire il flusso di una transazione.

// 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
}

Nel client le impostazioni TransactionFlowOption del servizio sulle operazioni vengono riflesse nella definizione generata dal client dell'interfaccia ICalculator. Inoltre, le impostazioni delle proprietà transactionFlow del servizio si riflettono nella configurazione dell'applicazione del client. Il client può selezionare il trasporto e il protocollo selezionando il endpointConfigurationNameappropriato.

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

Nota

Il comportamento osservato di questo esempio è lo stesso indipendentemente dal protocollo o dal trasporto scelto.

Dopo aver avviato la connessione al servizio, il client crea un nuovo TransactionScope che circonda le chiamate alle operazioni del servizio.

// 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");

Le chiamate alle operazioni sono le seguenti:

  • La richiesta Add passa la transazione richiesta al servizio e le azioni del servizio si verificano nell'ambito della transazione del client.

  • La prima richiesta Subtract trasferisce la transazione consentita al servizio e le azioni del servizio si verificano all'interno dell'ambito della transazione del cliente.

  • La seconda richiesta Subtract viene eseguita all'interno di un nuovo ambito di transazione dichiarato con l'opzione TransactionScopeOption.Suppress. In questo modo si elimina la transazione esterna iniziale del client e la richiesta non trasmette una transazione al servizio. Questo approccio consente a un client di rifiutare esplicitamente e proteggere dal trasferimento di una transazione a un servizio quando questo non è richiesto. Le azioni del servizio vengono eseguite nell'ambito di una transazione nuova e non connessa.

  • La richiesta di Multiply non inoltra una transazione al servizio perché la definizione dell'interfaccia ICalculator generata dal client include un TransactionFlowAttribute impostato su TransactionFlowOptionNotAllowed.

  • La richiesta di Divide non trasferisce una transazione al servizio poiché la definizione dell'interfaccia ICalculator generata nuovamente dal client non include un TransactionFlowAttribute. Le azioni del servizio si verificano di nuovo nell'ambito di un'altra transazione nuova e non connessa.

Quando si esegue l'esempio, le richieste e le risposte dell'operazione vengono visualizzate nella finestra della console client. Premere INVIO nella finestra client per arrestare il client.

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.

La registrazione delle richieste di operazione del servizio viene visualizzata nella finestra della console del servizio. Premere INVIO nella finestra client per arrestare il client.

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

Dopo un'esecuzione riuscita, l'ambito della transazione del cliente viene completato e tutte le azioni intraprese all'interno di tale ambito vengono confermate. In particolare, i 5 record annotati vengono salvati in modo permanente nel database del servizio. I primi 2 di questi si sono verificati nell'ambito della transazione del client.

Se si è verificata un'eccezione in qualsiasi punto del TransactionScope del client, la transazione non può essere completata. In questo modo, i record registrati all'interno di tale ambito non verranno sottoposti a commit nel database. Questo effetto può essere osservato ripetendo l'esecuzione dell'esempio dopo aver commentato la chiamata per completare il TransactionScopeesterno. In un'esecuzione di questo tipo vengono registrate solo le ultime 3 azioni (dalla seconda Subtract, le Multiply e le richieste di Divide) perché la transazione client non è passata a quelle.

Per configurare, compilare ed eseguire l'esempio

  1. Per compilare la versione C# o Visual Basic .NET della soluzione, seguire le istruzioni in Compilazione degli esempi di Windows Communication Foundation.

  2. Assicurarsi di aver installato SQL Server Express Edition o SQL Server e che la stringa di connessione sia stata impostata correttamente nel file di configurazione dell'applicazione del servizio. Per eseguire l'esempio senza usare un database, impostare il valore usingSql nel file di configurazione dell'applicazione del servizio su false.

  3. Per eseguire l'esempio in una configurazione con computer singolo o incrociato, seguire le istruzioni riportate in Esecuzione degli esempi di Windows Communication Foundation.

    Nota

    Per la configurazione tra computer, abilitare Distributed Transaction Coordinator usando le istruzioni riportate di seguito e usare lo strumento WsatConfig.exe di Windows SDK per abilitare il supporto di rete delle transazioni WCF. Per informazioni sulla configurazione di WsatConfig.exe, vedere Configurazione del supporto transazioni WS-Atomic.

Indipendentemente dal fatto che l'esempio venga eseguito nello stesso computer o in computer diversi, è 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 WCF.

Per configurare Microsoft Distributed Transaction Coordinator (MSDTC) per supportare l'esecuzione dell'esempio

  1. In un computer del servizio che esegue Windows Server 2003 o Windows XP, configurare MSDTC per consentire le transazioni di rete in ingresso seguendo queste istruzioni.

    1. Dal menu Start , passare a Pannello di controllo, quindi a Strumenti di amministrazionee successivamente a Servizi componenti.

    2. Espandi Component Services. Aprire la cartella Computer.

    3. Fare clic con il pulsante destro del mouse su Computer personale e selezionare Proprietà.

    4. Nella scheda MSDTC, fai clic su Configurazione di sicurezza.

    5. Verificare l'accesso DTC di rete e consentire l'ingresso .

    6. Fare clic su OK, quindi su per riavviare il servizio MSDTC.

    7. Fare clic su OK per chiudere la finestra di dialogo.

  2. In un computer del servizio che esegue Windows Server 2008 o Windows Vista, configurare MSDTC per consentire le transazioni di rete in ingresso seguendo queste istruzioni.

    1. Dal menu Start passare a Pannello di controllo, poi Strumenti di amministrazionee infine Servizi componenti.

    2. Espandi Component Services. Aprire la cartella Computer. Selezionare Distributed Transaction Coordinator.

    3. Fare clic con il pulsante destro del mouse su DTC Coordinator e selezionare Proprietà.

    4. Nella scheda sicurezza selezionare accesso DTC di rete e Consentiin ingresso .

    5. Fare clic su OK, quindi su per riavviare il servizio MSDTC.

    6. Fare clic su OK per chiudere la finestra di dialogo.

  3. Nel computer client configurare MSDTC per consentire le transazioni di rete in uscita:

    1. Dal menu Start, passare a Control Panel, quindi Strumenti di amministrazione, e quindi Servizi componenti.

    2. Fare clic con il pulsante destro del mouse su Computer personale e selezionare Proprietà.

    3. Sulla scheda MSDTC, cliccare su Configurazione di sicurezza.

    4. Controllare di accesso DTC di rete e Consentiin uscita.

    5. Fare clic su OK, quindi su per riavviare il servizio MSDTC.

    6. Fare clic su OK per chiudere la finestra di dialogo.